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>
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>