In the third installment of the Trinidad series, Matthias Wessendorf takes you on a tour of the Table and Tree components.

Apache MyFaces Trinidad is a huge JSF library that contains over 100 Ajax-based components. The first article of this series gave an introduction to some simple components, while the second article explained details about the Ajax API. This article will cover more complex components in detail.

Base classes for the Trinidad components

The Trinidad components don’t extend the standard components like UIData or UIInput. Trinidad introduces its own structure of base classes. The lowest common denominator is the standard abstract UIComponent class. From there, Trinidad goes its own way, as shown in the UML diagram (figure 1).

The UIXComponent is a pure abstract base class for all Trinidad components. UIXComponentBase is the base implementation of components for all of Trinidad, similar to UIComponentBase from the JSF standard. UIXComponentBase offers a number of features not supplied by the standard UIComponentBase class:

Use of FacesBean for better and easier state saving

Support of the LifecycleRenderer class for greater Renderer control over the lifecycle

Built-in support for Partial Page Rendering (see the second article in this series for a discussion of Partial Page Rendering) for both the "partialTriggers" attribute (declarative support for being a PPR target) and for triggering such components (for being the source of a PPR-causing event) when doing Ajax.

Subclassers that use FacesBean for their state do not need to override saveState() and restoreState().

State is optimized by default.

Future optimizations -- partly exposed today with markInitialState() -- can offer major state saving improvements.

The UIXComponentBase class differs from UIXComponent mainly in its use of FacesBeans to store all state. This offers a number of advantages:

Let’s take a closer look at some of these components.

Structured data handled with Trinidad

JSF doesn’t only offer a solid API to develop custom components, it also has a little API to work with the underlying data model, which is mainly used in the UIData component. However, this API is flexible and is extended by Apache MyFaces Trinidad, as shown in figure 2.

The JSF abstract class DataModel is the base class for all Trinidad data models that deal with “structured data.” The CollectionModel class is used by the <tr:table> and <tr:iterator> components. It extends the standard DataModel and adds support for rowKeys and sorting. Ordinary DataModels are still supported, and will automatically be wrapped into CollectionModels, but without the added functionality. The TreeModel understands how to iterate through an object graph, enter and leave containers, and identify rows of objects within each container. It extends CollectionModel to add support for container rows, which are entered and exited with enterContainer() and exitContainer() methods. Within a container, row indices are relative to the container. The ChildPropertyTreeModel is a concrete convenience class, but largely requires that you have the entire object model fully loaded.

The Trinidad Table component

The standard table does not support scrolling (navigation through the data set) by default. Several libraries offer help here by introducing a “scroller component,” which is wired to the actual table (UIData) component. With Trinidad, the scrolling feature (Ajax-enabled) is a built-in feature: ... <tr:table id="friends" value="#{container.friends}" var="friend" rows="5"> <tr:column headerText="Firstname"> <tr:outputText value="#{friend.firstName}"/> </tr:column> <tr:column headerText="Secondname"> <tr:outputText value="#{friend.secondName}"/> </tr:column> </tr:table> ...

The table looks pretty much like a regular table; however, the rows attribute tells the table to render only five rows in this case. Figure 3 shows the scrolling, done with Ajax.

When there are more rows available, the component renders some navigation options. To display all rows at once, set this attribute to 0. However, the default value is 25. Another cool attribute is emptyText which is used when the underlying data is empty. The <tr:table> offers support for sorting as well, as shown in figure 4.

To archive this, you just have to tweak the <tr:column> a little bit:

... <tr:column headerText="Firstname" sortProperty="firstName" sortable="true"> <tr:outputText value="#{friend.firstName}"/> <//tr:column> ...

This snippet enables the sorting, and tells the component that this column has to sort the “firstName” property of the underlying Java object.

Please, show me some details!

One of the key features of the Trinidad <tr:table> component is its detailStamp facet. This is useful to display or hide additional details of a particular row. When the details feature is enabled, a new column containing a toggle (per row) will render within the table. Once a toggle is activated, the details for that row are displayed and vise versa. The code is pretty simple: ... <tr:table var="friend" ...> </tr:column ...> ... </tr:column> <f:facet name="detailStamp" > <tr:panelGroupLayout layout="vertical" > <tr:outputText value="Street: #{friend.address.street}"/> <tr:outputText value="ZIP Code: #{friend.address.zip}"/> ... </tr:panelGroupLayout> </f:facet> </tr:table> ...

The detailStamp facet of the table contains a layout container (<tr:panelGroupLayout>) to group several <tr:outputText> components, which contain extra information on the underlying object (see figure 5). Again, the toggling is Ajax-enabled.

Data selection within a table

The selection feature of the <tr:table> (see figure 6) lets the user select one or more rows in the list.

The user can then perform some operation on the selected rows by activating an appropriate ActionSource component. For instance, they could click on a <tr:commandButton> component:

... <tr:table id="friends" rowSelection="multiple" ...> ... </tr:table> <tr:commandButton text="Invite for drink" action="#{container.select}" /> ...

The rowSelection attribute (default is none) tells the component that we want multiple selection. The rest of the table is similar to what you have already seen. Here is the callback for the selection, the select() method:

... public String select() { FacesContext context = FacesContext.getCurrentInstance(); UIXTable table = (UIXTable) context.getViewRoot().findComponent("friends"); RowKeySet rowKeySet = table.getSelectedRowKeys(); Iterator <Object> iterator = rowKeySet.iterator(); while(iterator.hasNext()) { Person friend = null; Object rowKey = iterator.next(); table.setRowKey(rowKey); friend = (Person) table.getRowData(); table.setRowKey(rowKey); // work on the selected entry... } rowKeySet.removeAll(); return null; } ...

The code looks for the “friends” component in the component tree and casts it to an instance of UIXTable. On the table object, we call getSelectedRowKeys() to get all the selected rows. We now iterate over them to do the desired work, such as sending an email for a party invitation. The rowKeySet.removeAll() call is important to remove all the selections on the table.

The Trinidad Tree component

The JSF standard contains only basic components that somewhat directly map to HTML 4.0.1 elements. This means that there is no tree component. However, several other JSF component libraries, like Tomahawk or IceFaces, offer a tree, since this is useful when creating a web application. The Trinidad component set also contains a tree component and some abstract model classes to handle structured tree data as first class citizens. The simplest possible version of a Trinidad tree is here:

... <tr:tree var="node" value="#{container.model}"> <f:facet name="nodeStamp"> <tr:outputText value="#{node.name}"/> </f:facet> </tr:tree> ...

The <tr:tree> component works much like a table, since both deal with structured data. The components share the same base class (UIXCollection) and use a similar model approach, as discussed above. The var attribute works much like it does with a vanilla JSF table (or the one in Trinidad). Figure7 shows an example of the tree.

Note that when you expand a node, the Trinidad library uses Ajax to display the new data. The key to using the <tr:tree> component is to use the provided TreeModel (or the convenience ChildPropertyTreeModel class). The value of the value attribute (#{container.model}) returns an object of the ChildPropertyTreeModel class, which is a useful basic subclass, but largely requires that you have the entire object model fully loaded. If you require lazy loading, you'll likely need a custom implementation of the abstract TreeModel: ... public TreeModel getModel() { Person john = new Person("John Smith"); Person kim = new Person("Kim Smith"); Person tom = new Person("Tom Smith"); Person zoe = new Person("Zoe Smith"); Person ira = new Person("Ira Wickrememsinghe"); Person mallika = new Person("Mallika Wickremesinghe"); john.getKids().add(kim); john.getKids().add(tom); tom.getKids().add(zoe); ira.getKids().add(mallika); List<Person> people = new ArrayList<Person>(); people.add(john); people.add(ira); return new ChildPropertyTreeModel(people, "kids"); } ...

The ChildPropertyTreeModel creates a TreeModel from a List of Java objects, and each object must have a getter method that returns the child List. All elements of your tree must be the same type. The class Person has a getKids()method that returns a List of more Person objects. You now create an object graph (the “people list”) and use this collection as an argument of the constructor. The second argument is the name of the property, which contains the List of elements (“kids”). Please note, that the ChildPropertyTreeModel also supports a java.util.Map-based structure. Such a Map needs a property with the name “kids” (in this example), which contains the other List of elements.

Conclusion

Trinidad offers a great set of JSF components. The components we discussed are very powerful and useful for every JSF project. Working with Tree/Table data is pretty simple and you see some great results very quickly. We’ll cover other aspects of Trinidad in the next article.