BETA: Event driven Table(s). Beta code may change in incompatible ways as we improve the implementation.
To add this to your project add the jar file to your classpath and add the following to your GWT module file:
<inherits name="org.mcarthur.sandy.gwt.table.Table"/>
These tables need the {@link org.mcarthur.sandy.gwt.event.list.client.EventList} module to funtion. The Table module inherits statement above will pull the EventList module in too. You will need to make sure the EventList code is available in your classpath.
Currently only {@link org.mcarthur.sandy.gwt.table.client.ObjectListTable} is provided. It requries that you manipulate your data model via an {@link org.mcarthur.sandy.gwt.event.list.client.EventList} instance. Because of the structure and automatic nature of {@link org.mcarthur.sandy.gwt.table.client.ObjectListTable} it requires a different setup code than using one of the GWT provided table implementations. Instead of manipulating the table by calling methods on the table, the ObjectListTable asks a {@link org.mcarthur.sandy.gwt.table.client.ObjectListTable.Renderer} you provide for information when it needs it.
First you need to have your data contained in an {@link org.mcarthur.sandy.gwt.event.list.client.EventList}. (If you're data is in a {@link java.util.List} see the {@link org.mcarthur.sandy.gwt.event.list.client.EventLists} class for how to wrapp a {@link java.util.List} in an {@link org.mcarthur.sandy.gwt.event.list.client.EventList}.)
public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } }
{@link org.mcarthur.sandy.gwt.table.client.ObjectListTable} needs you to provide a {@link org.mcarthur.sandy.gwt.table.client.ObjectListTable.Renderer} that can assemble the rows needed for an Object in your model, such as the Person object above. The render requires three methods, two of which can be empty if you do not need a header or footer in your table. The following render will create a header but not a footer on the table.
public class PersonRenderer implements ObjectListTable.Renderer { // Called when the table is attached to the page to build the header row(s) public void renderHeader(TableHeaderGroup headerGroup) { // headerGroup maps to the HTML 4 Table Row Group which isn't that commonly used // Create a Table Row in that Table Row Group TableRow tr = headerGroup.newTableRow(); headerGroup.add(tr); // Create a Table Header (TH) in that Table Row TableCell th = tr.newTableHeaderCell(); tr.add(th); // Add a widget to the cell th.add(new Label("Name"); // Create another cell th = tr.newTableHeaderCell(); th.add(new Label("Age"); } // This is called once for each object in the List public void render(Object obj, TableBodyGroup rowGroup) { Person person = (Person)obj; // Create a Table Row TableRow tr = rowGroup.newTableRow(); rowGroup.add(tr); // Create a Table Data (TD) for the person's name TableCell td = tr.newTableDataCell(); tr.add(td); td.add(new Label(person.getName()); // Create a Table Data (TD) for the person's age td = tr.newTableDataCell(); tr.add(td); td.add(new Label(Integer.toString(person.getAge())); } // Called when the table is attached to the page to build the footer row(s) public void renderFooter(TableFooterGroup footerGroup) { // do nothing if you don't want a Table header or footer. } }
If you need to modify the any part of the table rows after they have been rendered you'll need
to implement the {@link org.mcarthur.sandy.gwt.table.client.ObjectListTable.AttachRenderer}
interface. It provides pairs of onAttach
and onDetach
methods that you
can use add and remove event listeners at different points in the TableRowGroup's life cycle.
Do not be tempted to attach event listeners in a Renderer.render
method because you
will likely create a memory leak unless your table has static contents.
Because JavaScript does not support WeakReferences if your model (eg: Person above) has a
reference to your view (eg: the TableRowGroups) then each will reference each other potentially
pinning each in memory.
Now that we have a {@link org.mcarthur.sandy.gwt.event.list.client.EventList} of our objects and a render to create table rows for those objects we can create the {@link org.mcarthur.sandy.gwt.table.client.ObjectListTable}. Below we have an {@link org.mcarthur.sandy.gwt.event.list.client.EventList} of People and we create a {@link org.mcarthur.sandy.gwt.table.client.ObjectListTable} and add it to the page. The we add some more people and remove one and the table show have created and remove the correct rows.
public class PeopleApp implements EntryPoint { public void onModuleLoad() { // The event list used to manipulate the table // If you don't have a starting list you can use ObjectListTable.getObjects() and populate that. EventList people = ...; ObjectListTable table = new ObjectListTable(new PersonRenderer(), people); RootPanel.get().add(table); // Add some people, you could also use people.addAll() if you had a non-EventList from elsewhere such as RPC. people.add(new Person("Bill", 21)); people.add(new Person("Ted", 18)); people.add(new Person("Rufus", 40)); // To remove people just modify the list people.remove(1); // remove Ted, if he was 2nd in the list } }
GWT only provides an event mechanism for Widgets. Since the {@link org.mcarthur.sandy.gwt.table.client.ObjectListTable} and {@link org.mcarthur.sandy.gwt.table.client.TableCell} are subclasses of Widget events on these are provided by GWT. {@link org.mcarthur.sandy.gwt.table.client.TableRowGroup} and {@link org.mcarthur.sandy.gwt.table.client.TableRow} provide an event listerner interfaces you can take advantage of too to make your table more interactive. Do note that events will not be fired on row groups or rows until you add the first listener and huge tables with lots of listeners will slow the browser down.