OverviewSince 3.0 The ace:dataTable component renders an HTML table element. Rows are created from a List or DataModel object bound to the value property. The header and footer are rendered by ace:column component children, via their header/footer facets, or by their headerText / footerText properties. Optionally a child ace:columnGroup component definition can override these properties of the ace:column children to allow for column and row spanning configurations. The full list of features provided by the table and dependant components:
Getting StartedA relatively basic usage of ace:dataTable: <ace:dataTable var="single" value="#{viewBean.objectList}" stateMap="${viewBean.rowStateMap}" paginator="true" rows="5" selectionMode="multiple"> <ace:ajax event="select" execute="details @this" render="details @this" onSuccess="ice.ace.jq('#details').effect('highlight');" /> <ace:column headerText="UPC"> <h:outputText value="#{single.upc}"/> </ace:column> <ace:column headerText="Artist"> <h:outputText value="#{single.artist}" /> </ace:column> <ace:column headerText="Album"> <h:outputText value="#{single.album}"/> </ace:column> <ace:column headerText="Name"> <h:outputText value="#{single.name}"/> </ace:column> </ace:dataTable> Attributes
The var attribute specifies a request-scope attribute under which the model data for the current row will be exposed. The value attribute specifies model data for this table. Expected type is a List or DataModel. Lazy loading requires an instance of the LazyDataModel object. Sub-row expansion requires a model of type List<Map.Entry<Object, List>> where the Entry object make up the nodes of a tree; with the key being the value, and the List as possible child nodes. The stateMap attribute specifies a RowStateMap object. Instantiated by the user or the component, allows application layer manipulation of row properties like selection, visibility and expansion. Maps from table model objects to RowState objects that keep state for component features. See the RowStateMap API for full details. The paginator attribute specifies a boolean value indicating the use of pagination on this table. Default is false. The rows attribute specifies the number of rows to display per page. Default is 0, to show all rows.
ace:ajax - Configures the component select event to use ajax communication, executing and rendering itself and another area ('details') of the page. Should this communication succeed, the provided javascript snippet is executed. Without this tag, the table would by default cache selections until a submit required by another feature or the tableis submitted as part of a larger request, rather then submitting selections immediately. For more info see Selection Modes, Passive vs. Active. ace:column - Renders a td element (unless stacked) that represents a single column of data within a parent DataTable container. Event Listeners
Client Behaviours
Javascript APIICEfaces 3.xThe client side component object is exposed through the global variable name specified in the widgetVar attribute. ICEfaces 4+The "widgetVar" attribute on the ACE components has been removed in ICEfaces 4 and in its place a new "ice.ace.instance()" client JavaScript object lookup API has been introduced. The reason for this change is to enable lazy-initialization of the ACE component JavaScript objects to improve runtime performance and reduce browser memory use. var widget = ice.ace.instance('frm:componentId);
Data Model LifecycleThe following is a description of the creation of the DataModel from the object bound to the value property. During table execution, getDataModel() is called. If setDataModel(null) hasn't been called or this is the first time getDataModel() has been run (this request), getValue() is called to return an object to back our DataModel. The value returned from getValue() is optionally the value property of the table, as returned by a superclass implementation of getValue(), else it returns a filtered List of values. In all cases, the superclass return value has its hashCode calculated. If the superclass hashCode has changed since getValue() has last been called, we're in a situation where we may need to handle the creation of a TreeDataModel (so we flag a later check in getDataModel()) and if we've applied any filters to the previous DataModel created from the old value, we flag that we ought to refilter the model (to create a new set of filtered values) prior to rendering. If the superclass hashCode hasn't changed since getValue() has last been called, we return the filtered data set if one exists (avoiding the need to refilter) or return the superclass value. When returning to getDataModel() we skip the check in to handle TreeDataModel creation, and allow the getDataModel() superclass implementation to synthesize our model. Custom hashCode() and equals() MethodsBecause of the way the ace:dataTable data model works, it is necessary that all data objects (represented by rows) implement custom hashCode() and equals methods. It is not necessary to do so in all cases, but it's recommended and even required in some cases, especially for advanced features. It is imperative to do this when manipulating the table's RowStateMap and when working in lazy loading mode. The state of each row is stored in a RowState object, which contains information about the state of the row in regards to selection, expansion, edit mode, and visibility. RowStateMap implements Map<Object, RowState>, so it behaves like a regular map, where the key is the data object itself and the value is the state of the row. Thus, it is necessary for the ace:dataTable component to be able to identify each row uniquely across different requests. It is not reliable to depend on the default hashCode() method, because the returned value is prone to change at every lifecycle, since new object instances are created at every request, instead of preserving these same objects throughout the lifetime of the application or session. So, it is important to implement a hashCode() method that will always return the same value for an object, based on it's data (especially its id or whatever makes it unique) across requests. Because of the same reasons, it is necessary to add a custom equals() method that will compare the object's data to determine if two objects are equal or not. The default equals method simply checks for object references, but this is not enough in our case, since object references can change at any moment. This is especially important, in order to keep consistent row states, when working in the lazy loading mode, since row objects aren't kept in memory, but retrieved dynamically, so by correctly implementing equals() and hashCode() methods we can preserve the state of a row every time it is fetched from the external data source to be displayed, otherwise the row state map won't return the same RowState object when using the data object as key, since it'd have a different hash code at a different lifecyle. Lazy LoadingThe lazy loading mode is a convenient way to allow a table to have a large number of rows, without having to load all the data into memory, as is normally done. This mode requires some additional work from the application developer. First of all, the 'lazy' attribute must be set to true (paginator="true" will be necessary as well in most cases). Second, the value attribute must be bound to an instance of LazyDataModel (org.icefaces.ace.model.table.LazyDataModel), which the developer must implement. Actually, the only method to implement of this abstract class is the 'load' method. This method is invoked at every lifecycle. This method gets passed all the necessary information to determine which rows are to be rendered, and it is the job of the application developer to use this information and retrieve the rows to be displayed from the external data source. This information includes the index of the first row, the page size, the sorting criteria per column, and the filters used in each column. Thus, filtering and sorting are left to the application developer to implement, since the ace:dataTable component would require the entire data set to be in memory in order to apply its sorting and filtering logic. Note that when using multiple filters in a column, they will be passed as a JSON array string. For example, by looking at the index of the first row of the table (as when paginating) and by looking at the page size, the application developer can determine where to start looking in the external data source and how many rows to retrieve. A page size of zero (0) means that all rows are to be retrieved. Also, when implementing LazyDataModel it is necessary to set the total row count explicitly, according to how many rows are available in the external data source. Finally, the class used for the data objects must implement custom hashCode() and equals() methods, in order to keep the state of each row across lifecycles, as discussed above. This way, rows lazily loaded can still use all of the advanced features that ace:dataTable offers such as selection, panel expansion, row expansion, visibility, editing mode, etc. Selection Modes, Passive vs. ActiveThe table supports submitting selections and deselections passively, that is, storing them until submitted as part of an ajax request initiated by another feature of the table, or as part of communication initiated by another component in the view. The alternative is active selection, which performs an ajax communication for every (de)selection. This was typically configured in 2.1 and prior with the 'update', 'rowSelectUpdate' and 'rowDeselectUpdate' properties which defined targets on the page to render during selection, and implied use of active selection. Our new handling, which checks to see if there are any non-disabled ace:ajax tags applied to the component for the "select" event, and if so, enables 'active' selection, using the configuration of the ajax tag, or "select" event defaults. Custom StylingThe ace:dataTable component has some structural CSS that makes the table work well and be usable, and it also follows the current theme for colors, fonts, backgrounds and general look and feel. If custom styling is required, it is advised to define more specific CSS declarations for styling any node that composes the table. For example, to modify the header cells, a declaration can start with the root container's CSS class followed by a class used in header cells like .ui-datatable .ui-widget-header {/* CSS rules */}. This way only elements using the ui-wdiget-header class that are inside a data table will be affected, and not all the other nodes on the page that use the same class that are not inside a data table. Custom styling is more complex in the ace:dataTable than in other components. The style and styleClass are rendered on the root div node. So, it might not be possible to affect the whole table by simply specifying inline styling or a class name on the root container, as this is usually easily overridden by so many other necessary styling rules used by the table. The following table contains all the CSS class names used by all the nodes that compose the ace:dataTable.
Keyboard SupportKeyboard navigable sub-controls include Sorting, Filtering, Row Expansions and Row Editing.
Known IssuesWhen working in lazy mode and defining the columns using c:forEach, the LazyDataModel.load() method might receive an incorrect property name (the keys in the filters map and SortCriteria.getPropertyName()). This is because the EL expression is more complex and not possible to be resolved at render time, and it's also not possible to partially evaluate the EL expression to obtain the name of the property corresponding to a column. In order to work around this, use the lazyColumnKey attribute. In this attribute you can specify an arbitrary string to help you identify the column that is being sorted and/or filtered, in the LazyDataModel.load() method. Typically, one would use the same property name used in the sortBy and filterBy attributes. It has been known that when using the binding attribute in conjunction with filtering, some things don't work as expected. Specifically, the text fields in the column headers, where the filter values are entered, are cleared at each request, resulting in the filter action not working. This can be avoided in two ways. The first way to avoid it is to simply put the DataTable binding property in a request-scoped backing bean. The other way to avoid it is to simply bind the filterValue attributes in the columns to properties in a backing bean. For nested tables, the execute value in the 'expand' and 'contract' ajax events should be @form or @all for the expansion/contraction to work. Because of the complexity of the column pinning feature and the styling requirements of this and other features, it's no possible to support certain features at the same time as column pinning. This is summary of the features that work and don't work in conjunction with column pinning. Working
Not Working
Other ResourcesOther TipsThe request delay for filter events is about half a second and is hard-coded in the javascript. However, it's possible to increase this delay by using ace:ajax and providing an onStart script that looks like this: <ace:ajax event="filter" onStart="cfg.onstart = null; setTimeout(function() {ice.ace.ab(cfg);}, 500); return false;" /> This will increment the delay by the number of milliseconds specified in the setTimeout() call. The cfg object is always passed to the onStart callback (More information here: http://www.icesoft.org/wiki/display/ICE/Ajax), and it contains all the settings for the ajax request. So, what this piece of code is doing first is removing the onStart function from the ajax settings, in order to avoid an infinite loop, and then sending the request with an additional delay. Returning false makes sure to cancel the initial request attempt. The column pinning functionality only works in conjunction with the scrollable table functionality. The 'find' ajax event works differently than other ajax events. The table.ajaxFind() function must be invoked on the table instance on the client side to send a request to the server. This could be done via an external button or link, for example. Then, you have to register an ajax listener in your bean that takes an object of type DataTableFindEvent and then you do the find operation yourself, which should involve calling the findRow() method on the DataTable component on the server. An example of this can be found on the ace:dataTable > Find demo on the showcase. Related Components |
DataTable
© Copyright 2021 ICEsoft Technologies Canada Corp.