Projects‎ > ‎XBL‎ > ‎

Datatable new and old generations


Rationale

The datatable "old generation" can be considered a prototype developed before the new datatable component.

This document describes the differences between the two generations and can be used to migrate between versions.

Architecture

The old generation heavily relied on the YUI datatable component and performed a JavaScript client side transformation between a standard table generated by an xforms:repeat control into a YUI datatable.

The issue with this architecture is that the structure of the table was heavily modified by the YUI library and that the events handlers attached to the table cells were lost during the process, making it impossible to use XForms controls within the table. This wasn't efficient when using big paginated result sets either.

The new architecture do not rely on the YUI component any longer (the YUI datatable.js script isn't included any longer) even if it still uses the YUI datatable.css style sheet to keep the same look and feel. Most of the behavior is now controlled using XForms controls and JavaScript is used mainly to support scrollable tables and rezizeable columns.

Interface


The syntax of the bound element and its children has been cleaned up. The main differences are that while the old generation used (X)HTML classes to convey a number of information to the JavaScript layer, the new generation relies on attributes for this purpose.

Bound Element

The name and namespaces of the bound element have been updated:
  • Old generation: widget:table (where xmlns:widget="http://orbeon.org/oxf/xml/widget")
  • New generation: fr:datatable (where xmlns:fr="http://orbeon.org/oxf/xml/form-runner")
The old generation appearance="xxforms:data-table" attribute is deprecated. Its purpose was to control the appearance of generic tables, but the datatable component seems to be specific enough to deserve its own name...

The other attributes are not modified, except the scrollable, sorted and sortedBy attributes described below.

Scrollable Attribute

  • In the old generation, scrollable was a boolean. To define a scrollable datatable, you would write scrollable="true" and the table would become scrollable in both directions (horizontally and vertically).
  • In the new generation, scrollable can take the values "horizontal", "vertical" or "both" to give control over the direction(s) in which the datatable is scrollable.

Column Definitions

In old generation datatables, column definitions were described as classes of the XForms controls within the table cells:

        <widget:table id="table-repeat1" appearance="xxforms:data-table">
            <!-- Optional <thead>, otherwise try to build one from body -->
            <thead>
                <tr>
                    <th>AAA (s)</th>
                    <th>BBB (s)</th>
                    <th>CCC</th>
                </tr>
            </thead>
            <tbody>
                <tr repeat-nodeset="record">
                    <td>
                        <!-- Classes allow setting column properties -->
                        <xf:output ref="AAA" class="widget-sortable widget-resizeable"/>
                    </td>
                    <td>
                        <!-- Classes allow setting column properties -->
                        <xf:output ref="BBB" class="widget-sortable"/>
                    </td>
                    <td>
                        <!-- Classes allow setting column properties -->
                        <xf:output ref="CCC"/>
                    </td>
                </tr>
            </tbody>
        </widget:table>

In that example, the control definitions were the widget-sortable and widget-resizeable classes.

These definitions are now done using fr:* attributes on the header cells when they are present or on the body cells otherwise and this example becomes:

       <fr:datatable id="table-repeat1">
            <!-- Optional <thead>, otherwise try to build one from body -->
            <thead>
                <tr>
                    <th fr:sortable="true" fr:resizeable="true">AAA (s)</th>
                    <th fr:sortable="true">BBB (s)</th>
                    <th>CCC</th>
                </tr>
            </thead>
            <tbody>
                <tr repeat-nodeset="record">
                    <td>
                        <!-- Classes allow setting column properties -->
                        <xf:output ref="AAA"/>
                    </td>
                    <td>
                        <!-- Classes allow setting column properties -->
                        <xf:output ref="BBB"/>
                    </td>
                    <td>
                        <!-- Classes allow setting column properties -->
                        <xf:output ref="CCC"/>
                    </td>
                </tr>
            </tbody>
        </fr:datatable>

Or, with implicit header definitions:

       <fr:datatable id="table-repeat2">
            <!-- Optional <thead>, otherwise try to build one from body -->
            <tr repeat-nodeset="record">
                <td fr:sortable="true" fr:resizeable="true">
                     <xf:output ref="AAA">
                        <!-- Optional label used if there is no <head> -->
                        <xf:label>AAA (imp)</xf:label>
                    </xf:output>
                </td>
                <td fr:sortable="true">
                    <xf:output ref="BBB">
                        <!-- Optional label used if there is no <head> -->
                        <xf:label>BBB (imp)</xf:label>
                    </xf:output>
                </td>
                <td>
                    <xf:output ref="CCC">
                        <!-- Optional label used if there is no <head> -->
                        <xf:label>CCC (imp)</xf:label>
                    </xf:output>
                </td>
            </tr>
        </fr:datatable>

Sorted and SortedBy Old Generation Attributes Becomes fr:sorted

The purpose of these attributes was to indicate that a column is already sorted so the datatable adjusts its display accordingly.

For instance, to specify that data is sorted by date descending:

        <widget:table id="table-repeat1" appearance="xxforms:data-table" sortedByKey="Date" sortedByDir="descending">
            <!-- Optional <thead>, otherwise try to build one from body -->
            <thead>
                <tr>
                    <th>Date</th>
                    <th>Author</th>
                    <th>Title</th>
                </tr>
            </thead>
            <tbody>
                <tr repeat-nodeset="/atom:feed/atom:entry">
                    <td>
                        <!-- Classes allow setting column properties -->
                        <xf:output ref="atom:published" xxforms:format="format-dateTime(., '[M01]/[D01]/[Y] - [h01]:[m01]:[s01]')"
                            class="widget-sortable widget-resizeable"/>
                    </td>
                    <td>
                        <!-- Classes allow setting column properties -->
                        <a href="{atom:author/atom:uri}">
                            <xf:output value="atom:author/atom:name" class="widget-sortable widget-resizeable"/>
                        </a>
                    </td>
                    <td>
                        <!-- Classes allow setting column properties -->
                        <xf:output value="atom:title" class="widget-sortable widget-resizeable"/>
                    </td>
                </tr>
            </tbody>
        </widget:table>

This is replaced by a fr:sorted attribute in the corresponding column definition:

        <fr:datatable>
            <!-- Optional <thead>, otherwise try to build one from body -->
            <thead>
                <tr>
                    <th fr:sortable="true" fr:resizeable="true" fr:sorted="descending">Date</th>
                    <th fr:sortable="true" fr:resizeable="true">Author</th>
                    <th fr:sortable="true" fr:resizeable="true">Title</th>
                </tr>
            </thead>
            <tbody>
                <tr repeat-nodeset="/atom:feed/atom:entry">
                    <td>
                        <xf:output ref="atom:published" xxforms:format="format-dateTime(., '[M01]/[D01]/[Y] - [h01]:[m01]:[s01]')"/>
                    </td>
                    <td>
                         <a href="{atom:author/atom:uri}">
                            <xf:output value="atom:author/atom:name"/>
                        </a>
                    </td>
                    <td>
                         <xf:output value="atom:title"/>
                    </td>
                </tr>
            </tbody>
        </fr:datatable>

New fr:sortType attribute

A new fr:sortType attribute is available to override the sort type (text or number) guessed from the xforms:control datatype. For example:

                <td fr:sortable="true" fr:sortType="text" fr:resizeable="true">
                    <xf:output ref="AAA">
                        <xf:label>AAA (imp)</xf:label>
                    </xf:output>
                </td>

Behavior


The behavior between both generation is very similar except where the architecture change is exposed to the user (or developer).

Repeat Node Set


Sort and pagination now being done server side, these operations have an impact on the node set which wasn't the case with the old generation. In particular, contextual functions such as index() or position() are affected by this change and you can compare the behavior of the XForms sandbox samples "datatable-index" and "datatable-ng-index" to illustrate this difference:
  • For the old generation, the node set wasn't affected by sort and pagination, meaning that position() always returned the position of the node in the original (non paginated and unsorted) node set.
  • For the new generation, the node set is sorted and paginated, meaning that position() returns ths position of a node in the current page with the current sort order.
If that was necessary, it would be easy for the datatable component to expose an xxforms variable with the original (unsorted and unpaginated) nodeset so that the node could be analyzed in its "real" context.

Other (Minor?) Changes

  • In the old generation when the table is originally displayed, the XForms context is set on first row of the column, but the YUI component isn't aware of that and no row is highlighted untill the user clicks on a cell. This can be seen if you look at the "datatable-index" sample: when you load the page, the index is set to 1 but no row are highlighted.
  • In the new generation, there is no distinction between a YUI selection and an XForms selection anymore and when you load a table, its first row is selected.

  • In the old generation, the message that is displayed when you move your mouse over a header title was formated by the YUI.
  • In the new generation, it is formated as a xforms:hint.


Comments