Foundation
Project Documentation

Introduction

The Apache Trinidad component is used to display a list of structured data. For example, if we have a data structure called Person that has two properties - firstname and lastname, we could use a Table with two columns - one for firstname, and the other for lastname - to display a list of Person objects.

The Table component is similar to the standard UIData component in JSF, but includes a number of extra features, including support for identifying rows by key (instead of by index), built-in support for paging through large models, sorting the model, and selecting single or multiple items in the model.

The Table Model

The Apache Trinidad component uses a model to access the data in the underlying list. The specific model class is org.apache.myfaces.trinidad.model.CollectionModel. You may also use other model instances, e.g., java.util.List, array, and javax.faces.model.DataModel. The Table will automatically convert the instance into a CollectionModel.

To access a particular row in the list, first make that row current, and then call the getRowData() method on the Table. To make a row current, call the setRowIndex(...) method (on the Table) with the appropriate index into the list. Alternatively, call the setRowKey(...) method with the appropriate rowKey.

To obtain the total number of rows in the list, call the getRowCount() method on the Table. In the case where the model does not yet know the total number of rows that are available, getRowCount() will return -1.

The Table has an isRowAvailable() method that returns true if the current row ( see setRowIndex(...) ) is available. This method is especially useful when the total number of rows is unknown.

Columns

The immediate children of a Table component must all be <tr:column> components. Each visible ADF Column component creates a separate column in the Table.

Headers

Use the "header" facet on a Column to create the column header. You can also use the "headerText" attribute to set the column header. The following example creates a two-column table with the column headers - "Firstname" and "Lastname":

<tr:table>
  <tr:column>
    <f:facet name="header">
      <tr:outputText value="Firstname"/>
    </f:facet>
    ...
  </tr:column>
  <tr:column headerText="Lastname">
    ...
  </tr:column>
</tr:table>
        

Note that both ADF Faces and JSF HTML tags can be used inside of the ADF table.

Data

The child components of each Column display the data for each row in that column. The Column does not create child components per row; instead, each child is repeatedly rendered (stamped) once per row. Because of this stamping behavior, some components many not work inside the table. Anything that is just pure output, with no behavior, will work without problems, as will components that don't "mutate" even as they deliver events (for example, command components are fine). Components that mutate their state are affected, but any that implement EditableValueHolder are supported (this includes all form input controls from the JSF specification as well as ADF Faces input controls), as are UIXShowDetail components.

As each row is stamped, the data for the current row ( see getRowData() ) is copied into an EL reachable property. The name of this property is defined by the var property on the Table. Once the Table has completed rendering, this property is removed (or reverted back to its previous value). In the following example, the data for each row is placed under the EL property "row". Each Column displays the data for each row by getting further properties from the "row" property:

<tr:table var="row" value="#{myBean.allEmployees}">
  <tr:column>
    <tr:outputText value="#{row.firstname}"/>
  </tr:column>
  <tr:column>
    tr:outputText value="#{row.lastname}"/>
  </tr:column>
</tr:table>
        

Formatting

The Column component supports the following attributes related to formatting:

align
The type of alignment to use for this column. The legal values are "left", "right", "start", "end" and "center". Use "left" for left-justified, "right" for right-justified, and "center" for center-justified columns. For BiDi columns use "start" and "end". Use "start" (instead of "left") to left-align columns in a left-to-right locale, and right-align columns in a right-to-left locale. Similarly, use "end" (instead of "right") to right-align columns in a left-to-right locale, and left-align columns in a right-to-left locale.
width
The width of this column, e.g. "100px".
noWrap
Controls whether long lines of text in the column data should be wrapped.

Column Groups

<tr:column> tags can be nested to produce groups of columns. The header of a column group spans across all the columns it contains. The following example creates a column group that has the header "Name" and contains two sub columns with headers "First" and "Last":

  <tr:table var="row" value="#{myBean.employees}">
    <tr:column headerText="Name">
      <tr:column headerText="First">
        <tr:outputText value="#{row.firstname}"/>
      </tr:column>
      <tr:column headerText="Last">
        tr:outputText value="#{row.lastname}"/>
      </tr:column>
    </tr:column>
  </tr:table>
          

Row Headers

Columns can be rendered as row headers by setting the "rowHeader" attribute on <column> to be true. Row header columns must be the first columns in a table.

Range Navigation

When the list being displayed by a Table is huge, you can enable the Table to break up the list into ranges and display a single range at a time. Range controls are provided on the Table to let the user scroll to the next range, or to go back to the previous range. If the total size of the list is known, a control to let the user jump directly to a particular part of the list is also provided on the Table. Use the Table attributes "rows" and "first" to control the range navigation feature.

The maximum number of rows to display in a single range is controlled by the "rows" attribute, which defaults to a reasonable size (e.g., 25). To disable range navigation (and display all the rows on one page) set this attribute to 0. In the following example, the maximum number of rows to display per range is set to 10:

<tr:table rows="10" ...>

The current range to display is controlled by the "first" attribute. This attribute is an index (based at zero) of a row in the list. Each range starts with the row identified by "first", and only contains as many rows as indicated by the "rows" attribute. Initially, the "first" attribute defaults to zero, which displays the range with the first row at the top.

RangeChangeEvent

When the user changes the range (being displayed in the Table), the Table generates a RangeChangeEvent. This event includes the index (based at zero) of the row that should now be at the top of the range. The Table responds to this event by automatically changing the value of the "first" attribute to this index.

In some cases it is necessary to know when the user changes the range on the Table (e.g., when the user navigates forward it might be necessary to release any cached data created for a previous range). To receive notifications of when the user changes the range, a RangeChangeListener instance should be registered with the Table. This is done by calling the addRangeChangeListener method, or by using the rangeChangeListener MethodBinding on the Table. These listeners are called after the Table has changed the "first" attribute in response to the event.

Displaying Details

You can configure the Table to display or hide additional details of a particular row in response to a user gesture. When the details feature is enabled, a new column containing a toggle (per row) will render in the Table. When a toggle is activated, the details for that row are displayed. When a toggle is deactivated, the details for the row are hidden. The user can also display or hide the details for all rows at the same time (the controls for this feature are enabled by setting the "allDetailsEnabled" property to true.)

To enable the details feature set the "detailStamp" facet on the Table. Place the components that are used to show the details (of a row), inside this facet. In the following example, the Person's age is displayed in the details section:

<tr:table var="row" value="#{myBean.allEmployees}">
  <f:facet name="detailStamp">
    <tr:outputText value="#{row.age}"/>
  </f:facet>
</tr:table>
        

Usually, the default behavior of the Table (with respect to displaying and hiding details) is sufficient. In some cases, however, it might be desirable to programmatically display or hide the details for a particular row. This can be done via the RowKeySet object obtained from the Table by calling the getDisclosureState() method. First, make the relevant row current by calling setRowIndex(...) or setRowKey(...) on the Table, and then call add() or remove() on the RowKeySet object. Adding the row to the set displays the details of the row. Removing the row hides them.

RowDisclosureEvent

When the user hides or shows the details of a row, the Table generates a RowDisclosureEvent. This event has properties that return which rows were disclosed and which were undisclosed. The Table responds to this by expanding or collapsing the details of those rows.

You can register custom RowDisclosureListener instances (that can do post processing) on the Table component.

Selection

The selection feature of a Table 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 (e.g., by clicking on an ADF CommandButton). Use the "rowSelection" attribute on the Table to enable the selection feature (see the examples below).

There are two types of selection - single and multiple. The type of selection is determined by the value of the "selection" attribute. Use the value "single" to enable single selection, and "multiple" to enable multiple selection.

The current selection of rows on the Table can be programmatically inspected and modified via the RowKeySet object obtained by calling getSelectedRowKeys() on the Table. To programmatically change a selection, add or remove rowKeys from this Set. Alternatively, the appropriate row can be made current by calling setRowIndex(...) or setRowKey(...) on the Table, and then the selection of that row may be changed by calling add() or remove() on the RowKeySet.

Selection Example

In the following example, the performDelete method is called when the "Delete" button is clicked.

<tr:table binding="#{mybean.table}" rowSelection="multiple" ...>
  <f:facet name="footer">
      <tr:commandButton text="Delete" actionListener="#{mybean.performDelete}"/>
  </f:facet>
  ...
</tr:table>
        

The performDelete method iterates through all the selected rows and calls the markForDeletion() method on each one:

public void performDelete(ActionEvent action)
{
  UIXTable table = getTable();
  Iterator selection = table.getSelectedRowKeys().iterator();
  Object oldKey = table.getRowKey();
  while(selection.hasNext())
  {
    Object rowKey = selection.next();
    table.setRowKey(rowKey);
    MyRowImpl row = (MyRowImpl) table.getRowData();
    row.markForDeletion();
  }
  // restore the old key:
  table.setRowKey(oldKey);
}

// Binding methods for access to the table.
public void setTable(UIXTable table) { _table = table; }
public UIXTable getTable() { return _table; }
private UIXTable _table;
        

SelectionEvent

The <tr:table> component triggers SelectionEvents when the selection state is changed. The SelectionEvent reports which rows were just deselected and which rows were just selected. Listeners for this event may be registered on the table using the "selectionListener" attribute or by adding the listener to the table using the "addSelectionListener" method.

Sorting

The Table component supports sorting columns in ascending or descending order. A special UI indicator on a column header lets the user know that the column is sortable. When the user clicks on a column header to sort a previously unsorted column, the Table sorts the column data in ascending order. Subsequent clicks on the same header sort the data in the reverse order.

There are three requirements to enable sorting: the underlying table model must support sorting, the "sortProperty" and "sortable" attributes must be set on the column to enable the sort capability for that column.

To support sorting, the CollectionModel instance must implement the following methods:

public boolean isSortable(String propertyName)
public List getSortCriteria()
public void setSortCriteria(List criteria)
        

If the underlying model is not a CollectionModel, the Table automatically examines the actual data to determine which properties are sortable. Any column that has data that implements java.lang.Comparable are sortable. This automatic support cannot be nearly as efficient as coding sorting directly into a CollectionModel (for instance, by translating the sort into an "ORDER BY" SQL clause), but is sufficient for small data sets.

To associate a column with a particular property-name to be used for sorting purposes, use the "sortProperty" attribute on the column. To enable the UI for sorting a particular column, set the "sortable" property to true.

In the following example, both columns are sortable. Sorting the first column sorts by the "firstname" property; sorting the second column sorts by the "lastname" property.

<tr:table ...>
  <tr:column sortProperty="firstname" sortable="true">
    <f:facet name="header">
      <tr:outputText value="Firstname" />
    </f:facet>
    ...
  </tr:column>
  <tr:column>
    <f:facet name="header" sortProperty="lastname" sortable="true">
      <tr:outputText value="Lastname" />
    </f:facet>
    ...
  </tr:column>
</tr:table>
      

SortEvent

When the user clicks a sortable column header, the <tr:table> component generates a SortEvent. This event has a getSortCriteria() property that returns the criteria that the table must be sorted by.

The Table responds to this event by calling the setSortCriteria method on the underlying CollectionModel. Any registered SortListener instances will also be called.

Grid Lines and Banding

By default, the table draws both horizontal and vertical grid lines. These may be independently turned off by setting the "horizontalGridVisible" and/or "verticalGridVisible" attributes to "false".

Banding is a technique where groups of rows (or columns) are displayed with alternating background colors. This helps to differentiate between adjacent groups of rows (or columns).

The "rowBandingInterval" attribute on the Table controls how many consecutive rows form a row group (for the purposes of banding). If this is "0" all rows will have the same background color. If this is a positive number then adjacent row groups will have different background colors.

The "columnBandingInterval" attribute on the Table controls how many columns form a column group (for the purposes of banding). If this is "0" all columns will have the same background color. If this is a positive number then adjacent column groups will have different background colors.

In the following example, the row banding pattern is to have two rows with a light background, followed by two rows with a dark background. The pattern is repeated across all rows. Similarly the column banding pattern is to have alternating light/dark columns.

<tr:table rowBandingInterval="2" columnBandingInterval="1" ...>
  ...
</tr:table>