JSF Central - An Introduction to OpenFaces, Part 2 – DataTable and TreeTable
JSF Central

 
 Home 
 
 Products 
 
 Articles & Books 
 
 Resources 
An Introduction to OpenFaces
 
An Introduction to OpenFaces, Part 2 – DataTable and TreeTable
by Dmitry Pikhulya
06 Oct 2011 03:30 EDT

The second article in this series introduces the OpenFaces DataTable and TreeTable components. DataTable extends the standard JSF DataTable component with a multitude of additional features. TreeTable has many of the same features of DataTable but is targeted at displaying hierarchical data structures.


In the first article we discussed how to add OpenFaces to your application and you saw some of the basic components offered by OpenFaces. In this article we will continue reviewing the OpenFaces components; specifically, we’ll take a closer look at two of the most popular ones: DataTable and TreeTable. These components address common application development needs such as displaying and navigating over lists of business entities and hierarchical data structures. The usage scenarios for each component can be quite different, both in how the data should be presented and how it should be manipulated. During the evolution of OpenFaces these components were given customization options to meet a lot of real-case usage scenarios, which makes them some of the most flexible ones among their peers in other JSF libraries. In this article we'll explore the set of basic features provided by these components and get familiar with their APIs.

Introducing DataTable Component

The DataTable component, like several other OpenFaces components, is built on top of the API of the standard JSF DataTable component. It has a tag that is an extended version of the standard tag, which makes it really easy to migrate. Despite this simplicity, the component allows you to add a multitude of features such as:

  • Sorting
  • Pagination
  • Single and multiple selection with keyboard, mouse, and/or selection column(s)
  • Rich interactive filtering possibilities with customizable filter components, conditions, etc.
  • Usage of a universal filter query builder component named CompositeFilter
  • Data scrolling: vertical with frozen header(s), and horizontal with optional fixing of columns
  • Conditional styling of arbitrary rows/cells
  • Column reordering
  • Column resizing
  • Appearance customization for all parts of a table including row/column separators, rows, columns, headers, footers, selection, sorting, etc.
  • Efficient handling of large data sets
  • And a lot more – it’s really impossible to list all of the possibilities here that are available with this component. You can refer to the DataTable component page

More good news is that all of these features, plus more component-specific features, are applicable for the TreeTable component, which is similar to DataTable but is targeted at displaying hierarchical structures instead of linear ones. In the sections below, as we're looking at some of these features of DataTable, keep in mind that they also apply to TreeTable.

Let’s look at an example. Say we're creating some kind of financial software and we need to display a list of banks. The usual declaration using the standard DataTable component would be like this:

<h:dataTable value="#{MyBean.banks}" var="bank"> <h:column> <f:facet name="header"> <h:outputText value="Institution Name"> </f:facet> <h:outputText value="#{bank.name}"> </h:column> <h:column> <f:facet name="header"> <h:outputText value="State"> </f:facet> <h:outputText value="#{bank.state}"> </h:column> ... </h:dataTable>

As usual, in case of the extended components, we’ll just replace the "h" prefix with "o" to switch to the OpenFaces component. Now let’s enrich this table with some of the extended functionality.

Sorting and Filtering

First, we'll add the possibility to sort columns interactively, which can be done by adding the sortingExpression attribute to each of the column. This attribute should be specified as an expression that identifies the sortable value, and in most cases it is the same as the displayed value. In addition, we'll add a more interesting feature, which significantly simplifies exploring the data – the filtering feature. We'll make each column filterable by placing one of the available filter components inside the column's "subHeader" facet. This will display the filter component below the column’s header, which will allow the user to quickly search the data set by entering or selecting search criteria. There are three filter tags that can be used here:

For any of these filters, we can either place the appropriate tag without any attributes, or optionally we can customize the filter's appearance or parameters such as condition, etc. For now, we'll just use empty < o:inputTextFilter> and < o:dropDownField> tags for the "Name" and "State" columns respectively.

<o:dataTable value="#{MyBean.banks}" var="bank"> <o:column header="Institution Name" sortingExpression="#{bank.name}"> <f:facet name="subHeader"> <o:inputTextFilter> </f:facet> <h:outputText value="#{bank.name}"> </o:column> <o:column header="State" sortingExpression="#{bank.state}"> <f:facet name="subHeader"> <o:dropDownFilter> </f:facet> <h:outputText value="#{bank.state}"> </o:column> ... </o:dataTable>

Note that the "header" facet is replaced with a new header attribute here. You can still use the facet if needed, but the attribute is more convenient in most cases. Now that we have a table with filters for each of its columns, we can type in the filter field and see the table's data filtered on the fly. In case of <o:dropDownFilter>, we can also select from a list of possible filter values, which are automatically detected by the DataTable component. Figure 1 shows this process in action.

  Figure 1. DataTable filtering in action
Figure 1. DataTable filtering in action

The filters aren't only for use in the column's "subHeader" facet; you can place them in the column's footer, the table's header or footer, or even outside of the table. The filter tags themselves have a lot of customization options that let you modify the filter's behavior and appearance. For example you can change the search condition used by the filter, specify the expression which should be evaluated for each row when filtering, specify whether search should be case-sensitive or not, etc. As we'll see in the TreeTable section below, it's also possible to use one more powerful type of filter named CompositeFilter that acts as a query builder, and can be attached to both DataTable and TreeTable components. We can’t cover all of these features in this article, so please read the filtering documentation to get a deeper understanding of filtering capabilities and detailed instructions on their usage.

Item Selection

Another feature that we'll add to our table is the capability to select a row and bind the selected object to a backing bean. This can be done just by placing the < o:singleRowSelection> tag in the <o:dataTable> tag and configuring its attributes to customize selection behavior and appearance. For now, we'll just use the rowData attribute, which lets you both specify and receive the selected object. As a result, the table would be declared as follows:

<o:dataTable value="#{MyBean.banks}" var="bank" rowKey="#{bank.id}"> <o:singleRowSelection rowData="#{MyBean.selectedBank}"> <o:column ...> ... </o:dataTable>

Now you can select a row by clicking on it, or change the selection with the keyboard, and the data object corresponding to the selected row will be saved in the MyBean.selectedBank property upon form submission (or Ajax submission of the DataTable component alone). As you may have already guessed, specifying the row that will be selected when the table is loaded can be done just by providing the appropriate value from the same MyBean.selectedBank property.

Notice the rowKey attribute on the <o:dataTable> tag, which is optional and is usually required for the proper functionality of row selection and other interactive features (see the documentation for details).

You can also turn on multiple row selection using the <o:multipleRowSelection> tag, which is very similar to <o:singleRowSelection> but provides an API for multiple rows (e.g. it has a rowDatas attribute instead of a rowData attribute). These attributes provide customization options, such as whether the mouse and/or keyboard can be used to change the selection, which Ajax action should be performed upon changing the selection, and how the selection bar should look. You can read about these and other settings in the Row Selection documentation section.

Data Scrolling and Column Manipulation Magic

As we have already seen with the <o:singleRowSelection> tag, adding some key tags inside of the <o:dataTable> tag can turn on certain features, and, at the same time, provide a place for customizing those features. This approach of moving the customization attributes to child tags for certain parts of functionality is used throughout the library for some other components as well -- it makes the component's API both simple and powerful at the same time. Let’s add some more features with the same approach.

<o:dataTable value="#{MyBean.banks}" var="bank" rowKey="#{bank.id}"> <o:columnResizing resizingState="#{MyBean.columnResizingState}" autoSaveState="true"> <o:columnReordering> <o:scrolling> <o:singleRowSelection rowData="#{MyBean.selectedBank}"> <o:column...> ... </o:dataTable>

The first feature added in this example is the column resizing capability, which is turned on with the < o:columnResizing> tag. If the user has changed the column widths, the resizingState attribute lets you save the column widths between page visits, and the autoSaveState attribute declaration saves the resizing state on the fly with Ajax. The other two features added in this example are the drag&drop column reordering feature, and vertical scrolling. The appropriate tags don't have any attributes in this example, but it's possible to optionally customize the parameters; for instance, you can specify the way the columns' drag&drop process looks, and whether to enable features like horizontal scrolling and position saving for the content scrolling feature. .

Let's now make the final customizations and see what our table looks like. First, we'll combine several columns into a group by placing them into the < o:columnGroup> tag. The usage of this tag is quite intuitive and very similar to that of < o:column> tag. For now we'll just use the header attribute to display a common header that spans across all columns group's columns. And second, we'll add the column context menu by placing the empty < o:columnMenu> tag into the table's "columnMenu" facet. Of course menu contents and appearance can be customized but we'll use the default menu, which allows sorting, and showing/hiding columns.

<o:dataTable value="#{MyBean.banks}" var="bank" rowKey="#{bank.id}"> ... <o:column header="Institution Name" ...> <o:column header="Certificate Number" ...> <o:columnGroup header="Address"> <o:column header="City" ...> <o:column header="State" ...> <o:column header="Zip" ...> <o:column header="County" ...> </o:columnGroup> <o:column header="Average Asstes" ...> </o:dataTable>

You can see the table with all the previous extensions, plus some additional customizations, in figure 2. You can also try this example yourself in the online demo to see things like filtering, column reordering, resizing, and other features in action.

  Figure 2. DataTable with some of the added functionality, such as filtering, scrolling, selection, etc.
Figure 2. DataTable with some of the added functionality, such as filtering, scrolling, selection, etc.

You can get the full demo source code that contains this and other examples on the downloads page. We have just scratched the surface of the possibilities provided by the DataTable component here, and there are other features that couldn't fit the format of the article, so you can look at the DataTable documentation for more information, and try experimenting with it yourself.

Introducing TreeTable Component

The TreeTable component has a very similar API to the DataTable component. All of the features described above can also be used with TreeTable, although since it represents hierarchical data instead of linear, it has a different API for specifying the data source, and some more TreeTable specific configurations.

Data displayed in TreeTable is specified by placing either <o:dynamicTreeStructure> or <o:staticTreeStructure> tags inside of the <o:treeTable&g DataTable component paget; tag. The <o:dynamicTreeStructure> tag is the most commonly used tag, and here's how it can be declared in a TreeTable:

<o:treeTable var="message" expansionState="levelsExpanded:1"> <o:dynamicTreeStructure nodeChildren="#{MyBean.nodeChildren}" nodeHasChildren="#{MyBean.nodeHasChildren}" nodeKey="#{message.id}"> <o:treeColumn header="Subject"> <h:outputText value="#{message.subject}"> </o:treeColumn> <o:column header="From"> <o:outputText value="#{message.author}"> </o:column> ... </o:treeTable>

You can see that just like in the DataTable component, there's the var attribute that specifies a variable for the current row. This variable holds the current node object. Besides being available when declaring the column tags, this variable is used within the <o:dynamicTreeStructure> tag when constructing the displayed data. Here's how it works. The TreeTable traverses the entire displayed hierarchy and evaluates value bindings declared for the nodeChildren, nodeHasChildren, and nodeKey attributes for each node. The nodeHasChildren attribute expression is evaluated, to "ask" the application whether the current node (as defined by a variable specified with the var attribute) has child nodes. The nodeChildren attribute expression is evaluated to read the actual list of child nodes for the current node, and nodeKey just specifies the unique node identifier. The process starts by setting the current node variable to null and reading the root nodes, and then the operation is repeated for each of the nodes, until the required part of the tree is read. Here's an example of how MyBean.getNodeChildren() method can typically be declared:

public List<ForumMessage> getNodeChildren() { ForumMessage message = Faces.var("message", ForumMessage.class); return message != null ? message.getReplies() : getRootMessages(); }

The nodeHasChildren attribute is optional, and the nodeChildren attribute is used if it is omitted, but specifying this attribute allows minimizing database queries. The MyBean.getNodeHasChildren() method can be implemented in a similar way:

public boolean getNodeChildren() { ForumMessage message = Faces.var("message", ForumMessage.class); return getMessageHasReplies(message); }

You might have noticed that one of the columns in a TreeTable is declared using the <o:treeColumn> tag instead of the usual <o:column> tag –- this column has the same functionality as <o:column> but it also displays the tree structure with indentation and expansion buttons. The expansionState attribute of the <o:treeTable>tag specifies that only the first-level nodes are expanded when the page is loaded.

Just like the DataTable component, it's possible to add sorting, filtering, resizing, and other functionality using the same API. It's also worth noting that besides the simple column filters described in the previous section, it's possible to attach the CompositeFilter component to DataTable or TreeTable, which will allow the user to build complex filter criteria. After adding all of these features the TreeTable declaration will look like this:

<o:compositeFilter id="compositeFilter" for="forumTreeTable"> <o:treeTable id="forumTreeTable" var="message" expansionState="levelsExpanded:1"> <o:dynamicTreeStructure nodeChildren="#{MyBean.nodeChildren}" nodeHasChildren="#{MyBean.nodeHasChildren}" nodeKey="#{message.id}"> <o:scrolling> <o:columnResizing> <o:columnReordering> <o:treeColumn ...> ... </o:treeTable>

Figure 3 shows how such a TreeTable with a few more appearance customizations looks in the browser.

  Figure 3. TreeTable with scrolling and other features and a CompositeFilter attached
Figure 3. TreeTable with scrolling and other features and a CompositeFilter attached

You can see this example in action in the online demo, and study the documentation to get more familiar with the TreeTable component.

More Flexibility

Besides the functionality we’ve just seen, DataTable and TreeTable have many other features which are often useful for many real-life scenarios and design requirements.

One such feature is conditional customization of individual row(s) and/or cell(s), which gives you the following options:

  • Applying a specific style for certain rows/cells
  • Specifying individual event handlers for certain rows/cells
  • Specifying individual content for certain rows/cells
  • Merging several cells in a row into one cell that spans across several columns

All of these customizations are provided by the <o:row> and <o:cell> tags. You can place one or more <o:row> tags inside of the <o:dataTable> or <o:treeTable> tag, and each <o:row> tag can also optionally have one or more <o:cell> tags. Using these tags is quite straightforward. Both of them have the standard style and event attributes, and a condition attribute that lets you apply the appropriate declarations to rows/cells, according to your application's logic.

Here's an addition to the DataTable example we saw that highlights rows for banks with average assets greater than one million and marks the appropriate asset cell itself with bold font.

<o:dataTable var="bank"...> <o:row condition="#{bank.averageAssets gt 1000000}" style="background: LightGreen!important"> <o:cell columnIds="avgAssets" style="font-weight: bold"> </o:row> ... </o:dataTable>

As a result, the DataTable will look as displayed in figure 4.

  Figure 4. The modified DataTable example whose rows and cells are styled by condition
Figure 4. The modified DataTable example whose rows and cells are styled by condition

Similarly, you can declare event attributes such as onclick, or specify custom cell content just by placing appropriate components inside the <o:cell> tag. The following example shows how custom cell content and the span attribute of <o:cell> tag are used to display the first-level rows in the Rows & Cells Customization TreeTable demo.

<o:treeTable var="email" nodeLevelVar="level" ...> <o:row condition="#{level == 0}" style="background: #e6e7e8 !important;"> <o:cell span="6" styleClass="category_name"> <h:outputText value="#{email}" ...> </o:cell> </o:row> ... </o:treeTable>

You can see the detailed information on how to use the <o:row> and <o:cell> tags in the documentation.

Conclusion

We have covered quite a lot of the major capabilities of the DataTable and TreeTable components. Nevertheless, there are also a lot of other ways to adapt DataTable and TreeTable components to various usage scenarios and interface designs, such as pagination, handling large datasets, dynamic columns, selection columns, etc. You can read the comprehensive guide on all features in the DataTable and TreeTable documentation sections. Feel free to join the OpenFaces group if you need any help or assistance when using these or other components. In the next article we'll consider OpenFaces Ajax and validation frameworks, as well as OpenFaces scheduling components, whose customization possibilities are on a par with DataTable and TreeTable components.


Download: Sample Applications

Resources



RSS feed(all feeds)

The Editor's Desk
Podcasts
Inside Facelets
In the Trenches

Site version 1.83  Report web site problems

Copyright (C) 2003-2014 Virtua, Inc. All Rights Reserved. Java, JavaServer Faces, and all Java-based marks are trademarks or registered trademarks of Oracle Corporation. in the United States and other countries. Virtua, Inc. is independent of Oracle Corporation. All other trademarks are the sole property of their respective owners.