contrib:PopupLink Component Index contrib:TableColumns

contrib:Table
org.apache.tapestry.contrib.table.components.Table
Visual Component
 
Description
Provides a HTML <table> element. The Table component is a facade component in the Table family. Table allows you to present a sortable and pagable table simply and easily by using only this one component.

The Table component allows you to manipulate its appearance by allowing you to define the 'class' attributes of its internal elements. If you want to change the structure of the table, however, you can instead build your own using the lower level components TableView, TablePages, TableColumns, TableRows, and TableValues.

The Table component delegates the handling of the table model and related activities to the TableView, and more detailed information about the process can be found in the documentation of that class.

Providing the data

There are many ways to provide the component with the data it has to render, but here are the three major ones:

  1. The data is passed to the source parameter in the form of an array, a collection, or an iterator, and the table columns are defined using the columns parameter (see further for details). Both of these parameters will be evaluated at every request by default, so changes in the data or the table columns will be displayed immediately.

    The example below uses this method.

    This is the easiest and most straightforward approach. It has one performance limitation, however - if the table is sorting the data according to a given column, the sorting will be performed at every request. The next methods provide ways to resolve this possible performance issue.

  2. The data is passed to the source parameter via an object that implements the IBasicTableModel interface. Through that interface you are given the sorting column (if any) and the numbers of the items that will be displayed on the current page. You then need to provide the component with the corresponding data.

    This method allows you to perform the sorting in the database and load only the data that will be displayed on the current page (e.g. by using the ORDER BY, LIMIT, and OFFSET clauses in SQL) and hence it could be far more efficient.

  3. All of the information (data, columns, state) is packaged in an ITableModel and is passed to the tableModel parameter.

    This approach allows greatest flexibility, but is recommended only for advanced users of the Table components.

Defining the columns

If you define the table columns using the columns parameter, you can either provide a list of ITableColumn objects, each defining a column in the table, or you can define the columns using a string that describes each column.

Please see the example below as a demonstration of the use of the latter approach.

The string describing the columns must be formatted in the following way:

  • The column definitions in the string are separated by commas

  • Each column definition must be of one of the following types:

    id
    id:expression
    id:description:expression

    Here the id defines the identification of the column, the expression is an OGNL expression that extracts the column value from the row object, and description is the title of the column if it is not defined otherwise.

    Each column definition may be prefixed by the ! character, which identifies the column as non-sortable.

    If defined, a Block with a name that is starts with the column id and ends with ColumnValue will be used to render the column values. Similarly, a Block with a name that starts with the column id and ends with ColumnHeader will be used to render the column headers.

    Finally, the title of the column will be taken from translation strings of the component by using the column id as a key.

    Please see the LocaleSelection component for examples.

  • A column definition may also be of the type

    =expression

    in which case it identifies an OGNL expression that returns an ITableColumn object defining the column.

  • The full description string may be prefixed by the * character, which means that the table is to be rendered within a form, and the column headers must submit the form if they are clickable (i.e. if the column is sortable).

Here is an example of the use of a description string to define columns:

  columns="locale:toString(), =currencyColumn, verbosity:Verbosity:currentRowVerbosity, !delete"

See Also
TableColumns, TablePages, TableRows, TableValues, TableView
Parameters
Name Type Direction Required Default Description
source Object[]
Collection
Iterator
IBasicTableModel
in You must provide either both source and columns parameters or the tableModel parameter   The data to be displayed by the component. This parameter must be used in combination with the columns parameter. The parameter must be an array of values, a collection, an iterator, or an object implementing the IBasicTableModel interface.
columns String
ITableColumnModel
ITableColumn[]
List
Iterator
in   The table columns to be displayed. The parameter must be an array, a list, or an Iterator of ITableColumn objects, an ITableColumnModel, or a String describing the columns (see documentation).
tableModel ITableModel in   The ITableModel to be used to render the table. The model contains all of the information needed to render the table and gives greatest flexibility, but it may be harder to implement than simply using the source and columns parameters.
tableSessionStateManager ITableSessionStateManager in no A custom session state manager that reloads the data at each request if it is provided via the source and columns parameters or stores all of it in the session if it is provided via the tableModel parameter This is the session state manager that will control what part of the table model will be saved in the session state. It is then used to recreate the table model by using what was saved in the session.

You can use one of the stock implementations of ITableSessionStateManager to determine the session state behaviour, or you can define your own.

tableSessionStoreManager ITableSessionStoreManager in no null Determines how the session state (returned by the session state manager) will be saved in the session. If this parameter is null, then the state will be saved as a persistent property. If it is not null, then the methods of the interface will be used to save and load the state.
columnSettingsContainer IComponent in no container The container used to look up Blocks and messages when the source/columns mode is being used.
row Object out no   The value object of the current row being rendered.
column ITableColumn out no   The object representing the current column being rendered.
pageSize int in no 10 The number of records displayed per page.

This parameter is only used with the source and columns parameters.

initialSortColumn String in no null The id of the column to initially sort the table by. A value of null indicates no sorting.

This parameter is only used with the source and columns parameters.

initialSortOrder boolean in no false The order of the initial sorting. Set this parameter to false to sort in an ascending order and to true to sort in a descending one.

This parameter is only used with the source and columns parameters.

pagesDisplayed int in no 7 Determines the maximum number of pages to be displayed in the page list when the table has more than one page.

For example, if the table has 20 pages, and 10 is the current page, pages from 7 to 13 in the page list will be shown if this parameter has a value of 7.

arrowUpAsset IAsset in no   The image to use to describe a column sorted in an ascending order.
arrowDownAsset IAsset in no   The image to use to describe a column sorted in a descending order.
pagesClass String in no   The CSS class of the table pages.
columnsClass String in no   The CSS class of the table columns.
rowsClass String in no   The CSS class of the table rows.
valuesClass String in no   The CSS class of the table values.

Body: ignored
Informal parameters: allowed
Reserved parameters: none

Examples

<< < 7 8 9 10 11 12 13 > >>
Locale
Language  
Country
Variant
ISO3Language
ISO3Country
is is

isl
select
is_IS is IS
isl ISL select
it it

ita
select
it_CH it CH
ita CHE select
it_IT it IT
ita ITA select
it_IT_EURO it IT EURO ita ITA select
iw iw

heb
select
iw_IL iw IL
heb ISR select
ja ja

jpn
select
ja_JP ja JP
jpn JPN select


<style> td { text-align: center } </style>
<table jwcid="table@contrib:Table" width="90%"
    source="ognl:@java.util.Locale@getAvailableLocales()"
    columns="Locale:toString(), Language, Country, Variant, ISO3Language, ISO3Country, !select"/>
<span jwcid="selectColumnHeader@Block">&nbsp;</span>
<span jwcid="selectColumnValue@Block">
    <a jwcid="@DirectLink" listener="ognl:listeners.selectLocale" parameters="ognl:components.table.tableRow">select</a>
</span>
Further examples

You can find additional examples in the Tutorial as part of the Workbench application under the "Table" tab.

That page consists of two components -- the LocaleList component and the LocaleSelection component.

The LocaleList component allows you to view all Locales in a table (similar to the example above), as well as to choose Locales from the table and add them to your "selection".

The LocaleSelection component displays the selected Locales and provides additional information about them. It also allows Locales to be removed from the selection.

Even though the two components utilizing the Table are placed on a single page, they can operate without any interference from each other with no effort at all on part of the developer -- each table can be sorted independently, for example. This is a good illustration of the power of Tapestry's component approach.


Table Internals Guide

This section is for advanced uses of the Table components only.

There are two important elements that comprise the Table functionality -- the table model and the table components.

The table model classes and interfaces provide the Table with the ability to display and manipulate data from various different sources. Using them in various combinations and customizing them allows the developer to modify the behaviour of the table according to his needs.

The table components are responsible for displaying the data provided by the table model, and working with them allows the developer to radically change the appearance of the table if necessary.

1. The Model

This section will discuss the table model and how it can be used and modified to satisfy the needs of the developer. The next section will then concentrate on how the table components can be used to customize the appearance of the table as much as possible.

1.1. The Table Model Family of Classes

Even though using a table model can be very simple, as shown in the first example, behind the curtains it may consist of many different interfaces and classes that work together to allow you to modify different aspects of the table data representation and manipulation without a lot of effort.

Below you can see a UML Class diagram of the interfaces and classes in the Table Model family. The rest of this section will discuss how they can be used and modified as needed.

Table Model Class Diagram

1.2. The Table Model

The table model provides the information needed by the view to render the table. For example, the Table component may use its tableModel parameter to determine what to display.

The Tapestry components access the table model via the relatively simple ITableModel interface, which is the facade of the table model hierarchy. A variety of implementations of this interface can be created to target different cases.

The contrib library currently defines only one rudimentary implementation that can be used in the majority of situations -- the SimpleTableModel. While versatile, however, this implementation may not be suitable for all cases, and hence you may create your own whenever necessary.

Using the SimpleTableModel is very easy -- you just need to provide it with the data to be displayed and the columns that the table will have:

    Object[] data = ...;
    ITableColumn[] columns = ...;
    ITableModel model = new SimpleTableModel(data, columns);

This model can then be passed to the Table component, and it will display the data using the given columns.

1.3. The Data Model

The SimpleTableModel can also obtain its data via the ITableDataModel interface. This allows the source of data to be abstracted.

The contrib library provides two standard implementations of this interface. The SimpleListTableDataModel and the SimpleSetTableDataModel. As the names indicate, the former provides the table data from Lists and arrays, while the latter provides the data from Sets and other unordered Collections. Using the data models is very simple as well:

    ArrayList data = ...;
    ITableDataModel dataModel = new SimpleListTableDataModel(data);
    ITableModel model = new SimpleTableModel(dataModel, ...);

An example of using SimpleSetTableDataModel can be found in the LocaleSelection component in the Workbench. The storage of the selected locales there is implemented precisely using that table data model.

1.4. The Column Model

A column in the table model is defined using the ITableColumn interface. It provides the information needed to identify the column (the column name), to render the column (the columnRender that renders the heading, and the valueRenderer that renders each cell), and to sort the column (whether the column is sortable and what Comparator should be used for sorting the table).

The basic implementation of ITableColumn provided is SimpleTableColumn. It takes care of all mundane tasks, such as supplying a renderer for the column header, and allows you to define a column by only providing its name and a way to extract the column data from the row object by overriding the getColumnValue() method. This is demonstrated by the example below that also configures the column to be sortable:

    // Create a new sortable column with name and title "Full Name"
    ITableColumn fullNameColumn = new SimpleTableColumn("Full Name", true) { 
        public Object getColumnValue(Object value) {
            PersonInfo info = (PersonInfo) value;
            return info.getLastName() + ", " + info.getFirstName();
        }
    };

Other methods can be overridden as well to provide additional features, such as custom rendering, as it will be shown in the next section.

The above can be achieved in a simpler, but not as flexible manner by using ExpressionTableColumn. It inherits SimpleTableColumn, and allows you to create columns whose data is obtained via OGNL:

    // Create a new sortable column with name and title "Occupation"
    ITableColumn occupationColumn =  new ExpressionTableColumn("Occupation", "occupation", true);

    // Create a new sortable column with name and title "Full Name"
    ITableColumn fullNameColumn =  new ExpressionTableColumn("Full Name", "lastName + \", \" + firstName", true);

The table columns are supplied to the table model using a List -like container defined by the ITableColumnModel interface. The generic implementation provided for this interface is SimpleTableColumnModel, which contains an array of ITableColumn objects of any type.

One more specific implementation available is the ExpressionTableColumnModel , which containsExpressionTableColumn objects. It allows you to define the columns of your table quickly and easily, by providing a name and an OGNL expression for each one in a String array:

    // Generate a simple sorting column model that uses OGNL to get the column data
    ITableColumnModel objColumnModel = 
        new ExpressionTableColumnModel(new String[] {
            "Locale", "toString()",
            "Language", "language",
            "Country", "country",
            "Variant", "variant",
            "ISO Language", "ISO3Language",
            "ISO Country", "ISO3Country"
        }, true);

1.5. The Table State

The state of the table model consists of any data that is not related to the contents of the table, but instead carries information about its presentation. The standard table model supports two states -- the paging state (how many rows per page; which page we are on) and the sorting state (which column we are sorting on; in an ascending or a descending order).

The paging state is defined by the ITablePagingState interface and is implemented in its simplest form by SimpleTablePagingState. Similarly, the sorting state is defined by ITableSortingState and is implemented by SimpleTableSortingState.

The states are typically accessed and modified by the components presenting the table (e.g. the TablePages component can change the current page based on the user selection). The user can also modify them, which is often necessary at initialization:

    ITableModel model = new SimpleTableModel(data, columns);

    // Set page size to 15 elements and go to page 8
    model.getPagingState().setPageSize(15);
    model.getPagingState().setCurrentPage(8);

    // Sort by column 'price' in an ascending order
    model.getSortingState().setSortColumn("price", ITableSortingState.SORT_ASCENDING);

The SimpleTableModel  class keeps the two states together in the SimpleTableState object, which can be passed via the constructor and obtained at any time using the getState() method. This is often useful when storing the state by itself is necessary (see below).

1.6. The Session State Manager

In Web interfaces, unlike client-side GUIs, the information about the state of the presentation must be kept in the session object between requests. What has to be kept and what can be thrown away and recreated during the next request differs in each case, however. The policy of what to do typically involves a tradeoff between memory and CPU power, and hence it depends very much on the specific situation.

The ITableSessionStateManager interface is used to define precisely this policy. It defines what part of the table model has to be saved in the session and can recreate the table model using the saved information when the next request comes.

The Table and the TableView components have a tableSessionStateManager binding that allows such a manager to be supplied. When the table model is needed at the beginning of a new request, the component use the following procedure to obtain it:

  1. Load the session state (it will be null initially)
  2. Invoke recreateTableModel(sessionState) on the Session State Manager to try to recrate the table model
  3. If recreateTableMode() returns null, use the tableModel binding to obtain the table model

Immediately before rendering, the opposite procedure takes place:

  1. Invoke getSessionState(tableModel) on the Session State Manager to extract the part of the model to be saved
  2. Save the session state

Three standard implementations of the ITableSessionStateManager interface are provided that address the common cases that can be implemented without custom code. Each of them implements a different policy for extracting session state from the model and hence is suitable for some specific situations:

Session State Manager Description

FullTableSessionStateManager

Saves the entire table model in the session. This is the default manager.

This manager is the simplest to use as it brings to a minimum the need for additional coding. It has a number of disadvantages, however. First, everything in the table model must be Serializable since it is stored in the session and may be serialized at one point or another. Second, depending on the table data, its memory consumption per session could be quite significant.

For those reasons, it is probably not a good idea to use this manager if you are displaying large amounts of data, if you have a lot of users, or if the application needs to be clustered. On the other hand, it is perfect for quick and dirty work.

SimpleTableSessionStateManager

Saves only the table model state and assumes that the data and column models are constant.

This manager is designed to work with the SimpleTableModel . It stores only the model state (paging and sorting), and recreates the model using the data and column models that must be supplied via its constructor. As a result, the session memory consumption is low, but the CPU utilization may be high, since the data must be resorted at each request. This manager is best used when the data that needs to be displayed is constant.

An example of the use of the manager can be seen in the LocaleList component in the Workbench. That component displays all locales available to the JVM -- information that does not change and is too much to be stored in the session.

NullTableSessionStateManager

Saves nothing at all and forces the component to always load the table model from the tableModel binding.

This manager is typically used when the storing and creation of the table model needs to be delegated to the parent component.

One approach that is a good compromise between memory consumption and CPU load is to store in the session only the state and the IDs of the objects in the table. In this way CPU is not loaded, since re-sorting is no longer necessary, and the memory consumption is not excessive, since the IDs tend not to take a lot of space. Implementing this approach, however, requires a custom manager, and possibly a custom TableModel to limit the data loaded from the database only to the elements that will be displayed on the current page.

1.7. The Session Store Manager

While the Session State Manager determines what to store in the session, the Session Store Manager determines how to store data in the session. Normally the information that needs to be saved is stored using the Tapestry persistent property mechanism. If a Session Store Manager is provided, however, it is asked to take care of storing and loading of that information.

This is typically necessary in two specific cases:

  1. When the same table needs to be displayed on multiple pages. In that case the persistent property method will not work as desired, and the table state must be saved in the Visit instead.
  2. When the table is a part of a Block, and the Block is used on more than one page. In that case the tables displayed may be different, but a persistent page property will save their states at the same location. Hence a custom Session Store Manager is necessary.

The Session Store Managers implement the ITableSessionStoreManager interface. There are no default implementations of this interface -- the developer must supply his own.

2. Changing appearance

While the table model defines what data should be displayed in the table, it does not define how that data would look. The appearance is an important element of the web applications, however, and so the table components provide a number of ways to the developer to customize their looks.

2.1. Setting styles

The simplest way to modify how the table looks is by using CSS. The Table component provides a number of bindings allowing you to set the CSS class of all major elements of the table. Please have a look at Table's JavaDoc for details.

One interesting use of this approach is to define an evenOdd bean of type org.apache.tapestry.bean.EvenOdd and bind the rowsClass parameter of table to "beans.evenOdd.next". In this way odd rows in the table will receive the class "odd", and even rows will receive the class "even", which allows for different formatting of neighbouring rows.

2.2. Changing layout

The Table component is very easy to use, but it may become a "straight jacket" if a different layout of the table is required. In such a case, instead of Table, the developer could use the lower level table components described below:

Component Wrapped by Description
TableView   Wraps the whole table structure and manages the table model
TablePages TableView Displays the page navigation interface
TableColumns TableView Generates the column headers
TableRows TableView Enumerates all rows on the current page
TableValues TableRows Renders the values for each column of the current row

 The Table component itself is based on those components. Here is its template:

    <span jwcid="tableView">
        <span jwcid="condPages"><span jwcid="tablePages"/></span>
        <tr><span jwcid="tableColumns"/></tr>
        <tr jwcid="tableRows"><td jwcid="tableValues"/></tr>
    </span>

You can use those components in a similar way to create a layout that you want. You can change the location of the page navigation section, add other columns in the table manually, or insert custom formatting. You can even build your own components to replace some of the standard ones.

A good example of this approach is the LocaleList component in the Workbench. It modifies the layout slightly and adds a separate column in the table that is not defined the table model.

2.3. Changing renderers

Another way to change the appearance of the table is by using the ITableColumn ability to provide custom renderers for both column headers and values. By default, SimpleTableColumn uses RenderString to display values and the SimpleTableColumnComponent to render the headers. That component makes the header a link if the column is sortable, handles clicks on it, and displays an indicator if the column is currently used for sorting.

If is quite possible to use custom components to render the values or the headers in a different way. This is the approach taken by the LocaleSelection component in the Workbench. It uses Block sections in its template to render some columns differently (see the demo).

While using this mechanism is not hard at all, there are a number of common pitfalls that the developer must avoid. If you would like to go that route, please see how the SimpleTableColumnComponent and the LocaleSelection component are implemented and read the JavaDocs of org.apache.tapestry.components.BlockRenderer and org.apache.tapestry.ComponentAddress beforehand.

 


contrib:PopupLink Component Index contrib:TableColumns