12 Using Tables, Trees, and Other Collection-Based Components
This chapter describes how to display structured data in components that can iterate through collections of data and then display each row in the collection, using the ADF Faces table
, tree
and treeTable
, listView
, and carousel
components. If your application uses the Fusion technology stack, then you can use data controls to create these components. SeeCreating ADF Databound Tables, Displaying Master-Detail Data, and Using More Complex Databound ADF Faces Components in Developing Fusion Web Applications with Oracle Application Development Framework.
This chapter includes the following sections:
About Collection-Based Components
The basic understanding of what is collection-based components is explained. The additional functionalities, such as Customizing the toolbar, Active Data and others, also can be used along with ADF table and tree components.
ADF Faces provides components that you can use to iterate through and display collections of structured data. Instead of containing a child component for each record to be displayed, and then binding these components to the individual records, these components are bound to a complete collection, and they then repeatedly render one component (for example an outputText
component), by stamping the value for each record.
Structured data can be displayed as a simple table consisting of a number of rows and one column, using the ListView
component, or multiple columns using the ADF Faces table
component. Hierarchical data can be displayed either as tree structures using ADF Faces tree component, or in a table format, using ADF Faces tree table component. A collection of images can be displayed in a carousel component. Figure 12-1 shows the ADF Faces collection-based components.
Figure 12-1 ADF Faces Collection-Based Components
Description of "Figure 12-1 ADF Faces Collection-Based Components"
Tip:
When you do not want to use a table, but still need the same stamping capabilities, you can use the iterator tag. For example, say you want to display a list of periodic table elements, and for each element, you want to display the name, atomic number, symbol, and group. You can use the iterator tag as shown in the following example.
<af:iterator var="row" first="3" rows="3" varStatus="stat" value="#{periodicTable.tableData}" > <af:outputText value="#{stat.count}.Index:#{stat.index} of #{stat.model.rowCount}"/> <af:outputText label="Element Name" value="#{row.name}"/> <af:outputText label="Atomic Number" value="#{row.number}"/> <af:outputText label="Symbol" value="#{row.symbol}"/> <af:outputText label="Group" value="#{row.group}"/> </af:iterator>
Each child is stamped as many times as necessary. Iteration starts at the index specified by the first attribute for as many indexes specified by the row
attribute. If the row
attribute is set to 0
, then the iteration continues until there are no more elements in the underlying data.
Collection-Based Component Use Cases and Examples
Collection-based components are used to display structured information. For example, as shown in Figure 12-2, the Table tab in the File Explorer application uses a table to display the contents of the selected directory.
Figure 12-2 Table Component in the File Explorer Application
Description of "Figure 12-2 Table Component in the File Explorer Application"
Hierarchical data (that is data that has parent/child relationships), such as the directory in the File Explorer application, can be displayed as expandable trees using the tree component. Items are displayed as nodes that mirror the parent/child structure of the data. Each top-level node can be expanded to display any child nodes, which in turn can also be expanded to display any of their child nodes. Each expanded node can then be collapsed to hide child nodes. Figure 12-3 shows the file directory in the File Explorer application, which is displayed using a tree component.
Figure 12-3 Tree Component in the File Explorer Application
Description of "Figure 12-3 Tree Component in the File Explorer Application"
Hierarchical data can also be displayed using tree table components. The tree table also displays parent/child nodes that are expandable and collapsible, but in a tabular format, which allows the page to display attribute values for the nodes as columns of data. For example, along with displaying a directory's contents using a table component, the File Explorer application has another tab that uses the tree table component to display the contents, as shown in Figure 12-4.
Figure 12-4 Tree Table in the File Explorer Application
Description of "Figure 12-4 Tree Table in the File Explorer Application"
Like the tree component, the tree table component can show the parent/child relationship between items. And like the table component, the tree table component can also show any attribute values for those items in a column. Most of the features available on a table component are also available in tree table component.
You can add a toolbar and a status bar to tables, trees, and tree tables by surrounding them with the panelCollection
component. The top panel contains a standard menu bar as well as a toolbar that holds menu-type components such as menus and menu options, toolbars and toolbar buttons, and status bars. Some buttons and menus are added by default. For example, when you surround a table, tree, or tree table with a panelCollection
component, a toolbar that contains the View menu is added. This menu contains menu items that are specific to the table, tree, or tree table component.
Figure 12-5 shows the tree table from the File Explorer application with the toolbar, menus, and toolbar buttons created using the panelCollection
component.
Figure 12-5 TreeTable with Panel Collection
Description of "Figure 12-5 TreeTable with Panel Collection"
The listView
component is a light-weight table that allows you to display structured data in a list format. Unlike a table, it does not have columns, which allows you to easily present data in a variety of patterns, beyond a simple tabular layout.
The components that display the actual data are contained in a single child listItem
component. Figure 12-6 shows a listView
component that contains one child listItem
component. The listItem
component contains a mix of layout components, output components and button components.
Figure 12-6 The listView Component Uses listItem Components to Hold Data for Each Row
Description of "Figure 12-6 The listView Component Uses listItem Components to Hold Data for Each Row"
The listView
component can also display hierarchical data. When a component that is bound to the parent data is placed in the groupHeaderStamp
facet, that data is displayed in a header. Figure 12-7 shows how the alphabet letters, which are the parent data, are displayed in headers, while the child personnel data is displayed in rows below the parent.
Figure 12-7 Hierarchical Data Can be Displayed in Groups
Description of "Figure 12-7 Hierarchical Data Can be Displayed in Groups"
The carousel component displays a collection of images in a revolving carousel, as shown in Figure 12-8. Users can change the image at the front either by using the slider at the bottom or by clicking one of the auxiliary images to bring that specific image to the front.
Additional Functionality for Collection-Based Components
You may find it helpful to understand other ADF Faces features before you implement your collection-based components. Additionally, once you have added a collection-based component to your page, you may find that you need to add functionality such as validation and accessibility. Following are links to other functionality that table and tree components can use.
-
Customizing the toolbar: You can customize the toolbar included in the panelCollection component, which provides menus, toolbars, and status bars for the table and tree table components. For information about menus, toolbars, and toolbar buttons, see Using Menus, Toolbars, and Toolboxes.
-
Geometry management of the table width: If the table is a child to a component that stretches its children, then this width setting will be overridden and the table will automatically stretch to fit its container. For information about how components stretch, see Geometry Management and Component Stretching.
-
Active data: If your application uses active data, then you can have the data in your tables and trees update automatically, whenever the data in the data source changes. See Using the Active Data Service in Developing Fusion Web Applications with Oracle Application Development Framework.
Note:
If you wish to use active data, and your application uses ADF Business Components, then your tables must conform to the following:
-
The table or tree is bound to homogeneous data which contains only a single attribute.
-
The table does not use filtering.
-
The tree component's
nodeStamp
facet contains a singleoutputText
tag and contains no other tags.
-
-
Events: Collection-based components fire both server-side and client-side events that you can have your application react to by executing some logic. See Handling Events.
-
Partial page rendering: You may want a collection-based component to refresh to show new data based on an action taken on another component on the page. See Using the Optimized Lifecycle.
-
Personalization: Users can change the way the component displays at runtime (for example the user can reorder columns or change column widths), those values will not be retained once the user leaves the page unless you configure your application to allow user customization. For information, see Allowing User Customization on JSF Pages.
-
Accessibility: You can make your components accessible. See Developing Accessible ADF Faces Pages.
-
Automatic data binding: If your application uses the Fusion technology stack, then you can create automatically bound tables and trees based on how your ADF Business Components are configured. See Creating ADF Databound Tables and Displaying Master-Detail Data in Developing Web User Interfaces with Oracle ADF Faces.
Common Functionality in Collection-Based Components
Using the functionalities available with collection-based components you can perform many functionalities. Some of them are, display data in a row in an ADF table structure and in node in an ADF tree structure, or you can decide to deliver either one row or multiple rows of content to the client machine.
Collection-based component share many of the same functionality, such as how data is delivered and how data can be displayed and edited. It is important that you understand this shared functionality and how it is configured before you use these components.
Displaying Data in Rows and Nodes
Instead of containing a child component for each record to be displayed, and then binding these components to the individual records, collection-based components are bound to a complete collection, and they then repeatedly render one component (for example an outputText
component) by stamping the value for each record. For example, say a table contains two child column components. Each column displays a single attribute value for the row using an output component and there are four records to be displayed. Instead of binding four sets of two output components to display the data, the table itself is bound to the collection of all four records and simply stamps one set of the output components four times. As each row is stamped, the data for the current row is copied into the var
attribute on the table, from which the output component can retrieve the correct values for the row. For information about how stamping works, especially with client components, see Accessing Client Collection Components.
The following example shows the JSF code for a table whose value for the var
attribute is row
. Each outputText
component in a column displays the data for the row because its value is bound to a specific property on the variable.
<af:table var="row" value="#{myBean.allEmployees}"> <af:column> <af:outputText value="#{row.firstname}"/> </af:column> <af:column> af:outputText value="#{row.lastname}"/> </af:column> </af:table>
Collection-based components uses a Collection
Model
class to access the data in the underlying collection. This class extends the JSF DataModel
class and adds on support for row keys and sorting. In the DataModel
class, rows are identified entirely by index. This can cause problems when the underlying data changes from one request to the next, for example a user request to delete one row may delete a different row when another user adds a row. To work around this, the CollectionModel
class is based on row keys instead of indexes.
You may also use other model classes, such as java.util.List
, array
, and javax.faces.model.DataModel
. If you use one of these other classes, the table component automatically converts the instance into a CollectionModel
class, but without the additional functionality. For information about the CollectionModel
class, see the MyFaces Trinidad Javadoc at http://myfaces.apache.org/trinidad/trinidad-1_2/trinidad-api/apidocs/index.html
.
Note:
If your application uses the Fusion technology stack, then you can use data controls to create tables and the collection model will be created for you. See Creating ADF Databound Tables in Developing Web User Interfaces with Oracle ADF Faces.
Content Delivery
The collection components are virtualized, meaning not all the rows that are there for the component on the server are delivered to and displayed on the client. You configure collection components to fetch a certain number of rows at a time from your data source. The data can be delivered to the components immediately upon rendering, when it is available, or lazily fetched after the shell of the component has been rendered (by default, the components fetch data when it is available), as specified by the setting the ContentDelivery
attribute on the component.
With immediate delivery, the data is fetched during the initial request. With lazy delivery, when a page contains one or more collection components, the page initially goes through the standard lifecycle. However, instead of fetching the data during that initial request, a special separate partial page rendering (PPR) request is run, and the number of rows set as the value of the fetch size for the component is then returned. Because the page has just been rendered, only the Render Response phase executes for the components, allowing the corresponding data to be fetched and displayed. When a user's actions cause a subsequent data fetch (for example scrolling in a table for another set of rows), another PPR request is executed.
When content delivery is configured to be delivered when it is available, the framework checks for data availability during the initial request, and if it is available, it sends the data to the component and inlines it for rendering. If it is not available, the data is loaded during the separate PPR request, as it is with lazy delivery. Note that in JDeveloper releases prior to 12.2.1.0, two separate requests are required to send the data and to inline the data upon rendering the component. If you prefer the content delivery performance of an earlier release, you can set the ContentDelivery
attribute to lazy
; this will force inlining of the data only during a subsequent.
Note:
If your application does not use the Fusion technology stack, then you must explicitly add support for whenAvailable
to your CollectionModel
implementation. For an example, see the WhenAvailableData.java
managed bean in the Faces demo application.
If your application does use the Fusion technology stack, then the CollectionModel
implementation created for you automatically uses these APIs.
Performance Tip:
Lazy delivery should be used when a data fetch is expected to be an expensive (slow) operation, for example, slow, high-latency database connection, or fetching data from slow nondatabase data sources like web services. Lazy delivery should also be used when the page contains a number of components other than a table, tree, or tree table. Doing so allows the initial page layout and other components to be rendered first before the data is available.
Immediate delivery should be used if the table, tree, or tree table is the only context on the page, or if the component is not expected to return a large set of data. In this case, response time will be faster than using lazy delivery (or in some cases, simply perceived as faster), as the second request will not go to the server, providing a faster user response time and better server CPU utilizations. Note however that only the number of rows configured to be the fetch block will be initially returned. As with lazy delivery, when a user's actions cause a subsequent data fetch, the next set of rows are delivered.
When available delivery provides the additional flexibility of using immediate when data is available during initial rendering or falling back on lazy when data is not initially available. Starting in the JDeveloper 12.2.1.0 release, when available is the default delivery content delivery mode.
The number of rows that are displayed on the client are just enough to fill the page as it is displayed in the browser. More rows are fetched as the user scrolls the component vertically (or if configured to page instead of scroll, when the user navigates to another set of rows). The fetchSize
attribute determines the number of rows requested from the client to the server on each attempt to fill the component. For a table, the default value is 25. So if the height of the table is small, the fetch size of 25 is sufficient to fill the component. However, if the height of the component is large, there might be multiple requests for the data from the server. Therefore, the fetchSize
attribute should be set to a higher number. For example, if the height of the table is 600 pixels and the height of each row is 18 pixels, you will need at least 45 rows to fill the table. With a fetchSize
of 25, the table has to execute two requests to the server to fill the table. For this example, you would set the fetch size to 50.
However, if you set the fetch size too high, it will impact both server and client. The server will fetch more rows from the data source than needed and this will increase time and memory usage. On the client side, it will take longer to process those rows and attach them to the component.
Performance Tip:
Reduce fetchSize
whenever possible.
By default, on a desktop device, tables render a scroll bar that allows the users to scroll through the rows of data. Instead, when you want to configure the table to be paginated, you can set the scrollPolicy
attribute to page
. A paginated table displays a footer that allows the user to jump to specific pages of rows, as shown in Figure 12-9.
When the viewport is too narrow to display the complete footer, the table displays a compact footer that shows only the page currently displayed and the navigation buttons, as shown in Figure 12-10.
Figure 12-10 Paginated Table in Compact Mode
Description of "Figure 12-10 Paginated Table in Compact Mode"
Note:
By default, on tablet devices, tables are rendered to display the table as paginated and therefore do not display a scroll bar. If instead, you want to enable scroll bars so the table automatically loads the next set of rows when the user scrolls to the bottom of the table, you can set the scrollPolicy
attribute to scroll
. This option on tablets results in a behavior called implicit high-water mark scrolling and closely resembles the way virtualized touch scrolling (scrolling in both horizontal and vertical directions) behaves on tablet devices. Regular virtualized scrolling which results from setting scrollPolicy
to scroll
on a desktop machine is not implemented on tablets due to performance problems with virtualized scrolling of table data on tablets. You can, however, customize the caching behavior of implicit high-water mark scrolling by specifying the number of rows to cache to minimize database round-trips by setting a value for the maxClientRows
attribute.
As with a table configured to scroll, the number of rows on a page is determined by the fetchSize
attribute.
Note:
By default, if you configure your tables to use scroll bars, on iOS operating systems, the scroll bars appear only when you mouseover the content. You can configure your application so that this same behavior occurs on other operating systems as well, by setting the oracle.adf.view.rich.table.scrollbarBehavior
parameter in the web.xml
file. See Scrollbar Behavior in Tables.
Note:
You can hide the scroll bar using the -tr-overflow-style: autohiding-scrollbar
skinning property. For example:
af|table { -tr-overflow-style: autohiding-scrollbar }
For information about skins, see Customizing the Appearance Using Styles and Skins.
You can also configure the set of data that will be initially displayed using the displayRow
attribute. By default, the first record in the data source is displayed in the top row or node and the subsequent records are displayed in the following rows or nodes. You can also configure the component to first display the last record in the source instead. In this case, the last record is displayed in the bottom row or node of the component, and the user can scroll up to view the preceding records. Additionally, you can configure the component to display the selected row. This can be useful if the user is navigating to the component, and based on some parameter, a particular row will be programmatically selected. When configured to display the selected row, that row will be displayed at the top of the table and the user can scroll up or down to view other rows.
Note:
You cannot use JavaScript to dynamically size a table or tree. The height of tables, trees and treetables is set the first time they are rendered and cannot be changed using JavaScript APIs.
Row Selection
You can configure selection to be either for no rows, for a single row, or for multiple rows using the rowSelection
attribute (the carousel
component does not allow multiple row selection). This setting allows you to execute logic against the selected rows. For example, you may want users to be able to select a row in a table or a node in a tree, and then to click a button that navigates to another page where the data for the selected row is displayed and the user can edit it.
Note:
If you configure your component to allow multiple selection, users can select one row and then press the shift key to select another row, and all the rows in between will be selected. This selection will be retained even if the selection is across multiple data fetch blocks. Similarly, you can use the Ctrl key to select rows that are not next to each other.
For example, if you configure your table to fetch only 25 rows at a time, but the user selects 100 rows, the framework is able to keep track of the selection.
When the selected row (or node) of a component changes, the component triggers a selection event. This event reports which rows were just deselected and which rows were just selected. While the components handle selection declaratively, if you want to perform some logic on the selected rows, you need to implement code that can access those rows and then perform the logic. You can do this in a selection listener method on a managed bean. See What You May Need to Know About Performing an Action on Selected Rows in Tables.
Performance Tip:
Users can navigate through the table using a mouse and the scrollbar, or using the up and down arrow keyboard keys. By default, a selection event is immediately fired when the user clicks a row. If the user is navigating through the rows using the arrow keys, this means that a selection event will be fired for each row, as the user navigates.
If you expect users to navigate through the table using the keys, you can set the delaySelectionEvent
attribute to true
, so that there is a 300 millisecond delay before the selection event is fired. If the user navigates to another row within the 300 milliseconds, the selection event is canceled.
Editing Data in Tables, Trees, and Tree Tables
You can choose the component used to display the actual data in a table, tree, or tree table. For example, you may want the data to be read-only, and therefore you might use an outputText
component to display the data. Conversely, if you want the data to be able to be edited, you might use an inputText
component, or if choosing from a list, one of the SelectOne
components. All of these components are placed as children to the column component (in the case of a table and tree table) or within the nodeStamp
facet (for a tree).
When you decide to use components whose value can be edited to display your data, you have the option of having the table, tree, or tree table either display all rows as available for editing at once, or display all but the currently active row as read-only using the editingMode
attribute. For example, Figure 12-11 shows a table whose rows can all be edited. The page renders using the components that were added to the page (for example, inputText
, inputDate
, and inputComboBoxListOfValues
components).
Figure 12-11 Table Whose Rows Can All Be Edited
Description of "Figure 12-11 Table Whose Rows Can All Be Edited"
Figure 12-12 shows the same table (that is, it uses inputText
, inputDate
, and inputComboBoxListOfValues
components to display the data), but configured so that only the active row displays the editable components, by setting editingMode
to clickToEdit
. Users can then click on another row to make it editable (only one row is editable at a time). Note that outputText
components are used to display the data in the noneditable rows, even though the same input components as in Figure 12-11 were used to build the page. The only row that actually renders those components is the active row.
Note:
When you seteditingMode
to readOnly
, all the components in the table will be rendered as read only, including input components. Also when the entire table is selected, the editingMode
will be set to readOnly
.
Figure 12-12 Table Allows Only One Row to Be Edited at a Time
Description of "Figure 12-12 Table Allows Only One Row to Be Edited at a Time"
The currently active row is determined by the activeRowKey
attribute on the table. By default, the value of this attribute is the first visible row of the table. When the table (or tree or tree table) is refreshed, that component scrolls to bring the active row into view, if it is not already visible. When the user clicks on a row to edit its contents, that row becomes the active row.
When you allow only a single row (or node) to be edited, the table (or tree or tree table) performs PPR when the user moves from one row (or node) to the next, thereby submitting the data (and validating that data) one row at a time. When you allow all rows to be edited, data is submitted whenever there is an event that causes PPR to typically occur, for example scrolling beyond the currently displayed rows or nodes.
Note:
Trees and tables support browser copy and paste. When you mouse over a text field that can be copied, the cursor displays an I-bar. When you click the mouse to copy the text, that row becomes selected.
Not all editable components make sense to be displayed in a click-to-edit mode. For example, those that display multiple lines of HTML input elements may not be good candidates. These components include:
-
SelectManyCheckbox
-
SelectManyListBox
-
SelectOneListBox
-
Select
OneRadio
-
SelectManyShuttle
Performance Tip:
For increased performance during both rendering and postback, you should configure your table to allow editing only to a single row.
When you elect to allow only a single row to be edited at a time, the page will be displayed more quickly, as output components tend to generate less HTML than input components. Additionally, client components are not created for the read-only rows. Because the table (or tree, or tree table) performs PPR as the user moves from one row to the next, only that row's data is submitted, resulting in better performance than a table that allows all cells to be edited, which submits all the data for all the rows in the table at the same time. Allowing only a singe row to be edited also provides more intuitive validation, because only a single row's data is submitted for validation, and therefore only errors for that row are displayed.
Using Popup Dialogs in Tables, Trees, and Tree Tables
You can configure your table, tree, or tree table so that popup dialogs will be displayed based on a user's actions. For example, you can configure a popup dialog to display some data from the selected row when the user hovers the mouse over a cell or node. You can also create popup context menus for when a user right-clicks a row in a table or tree table, or a node in a tree. Additionally, for tables and tree tables, you can create a context menu for when a user right-clicks anywhere within the table, but not on a specific row.
Tables, trees, and tree tables all contain the contextMenu
facet. You place your popup context menu within this facet, and the associated menu will be displayed when the user right-clicks a row. When the context menu is being fetched on the server, the components automatically establish the currency to the row for which the context menu is being displayed. Establishing currency means that the current row in the model for the table now points to the row for which the context menu is being displayed. In order for this to happen, the popup
component containing the menu must have its contentDelivery
attribute set to lazyUncached
so that the menu is fetched every time it is displayed.
Tip:
If you want the context menu to dynamically display content based on the selected row, set the popup content delivery to lazyUncached
and add a setPropertyListener
tag to a method on a managed bean that can get the current row and then display data based on the current row:
<af:tree value="#{fs.treeModel}" contextMenuSelect="false" var="node" ..> <f:facet name="contextMenu"> <af:popup id="myPopup" contentDelivery="lazyUncached"> <af:setPropertyListener from="#{fs.treeModel.rowData}" to="#{dynamicContextMenuTable.currentTreeRowData}" type="popupFetch" /> <af:menu> <af:menu text="Node Info (Dynamic)"> <af:commandMenuItem actionListener= "#{dynamicContextMenuTable.alertTreeRowData}" text= "Name - #{dynamicContextMenuTable.currentTreeRowData.name}" /> <af:commandMenuItem actionListener= "#{dynamicContextMenuTable.alertTreeRowData}" text= "Path - #{dynamicContextMenuTable.currentTreeRowData.path}" /> <af:commandMenuItem actionListener= "#{dynamicContextMenuTable.alertTreeRowData}" text="Date - #{dynamicContextMenuTable.currentTreeRowData.lastModified}" /> </af:menu> </af:menu> </af:popup> </f:facet> ... </af:tree>
The code on the backing bean might look something like this:
public class DynamicContextMenuTableBean { ... public void setCurrentTreeRowData(Map currentTreeRowData) { _currentTreeRowData = currentTreeRowData; } public Map getCurrentTreeRowData() { return _currentTreeRowData; } private Map _currentTreeRowData; }
Tables and tree tables contain the bodyContextMenu
facet. You can add a popup that contains a menu to this facet, and it will be displayed whenever a user clicks on the table, but not within a specific row.
For information about creating context menus, see Declaratively Creating Popups.
Accessing Client Collection Components
With ADF Faces, the contents of collection-based components are rendered on the server. There may be cases when the client needs to access that content on the server, including:
-
Client-side application logic may need to read the row-specific component state. For example, in response to row selection changes, the application may want to update the disabled or visible state of other components in the page (usually menu items or toolbar buttons). This logic may be dependent on row-specific metadata sent to the client using a stamped
inputHidden
component. In order to enable this, the application must be able to retrieve row-specific attribute values from stamped components. -
Client-side application logic may need to modify row-specific component state. For example, clicking a stamped command link in a table row may update the state of other components in the same row.
-
The peer may need access to a component instance to implement event handling behavior (for information about peers, see About Using ADF Faces Architecture). For example, in order to deliver a client-side action event in response to a mouse click, the
AdfDhtmlCommandLinkPeer
class needs a reference to the component instance which will serve as the event source. The component also holds on to relevant state, including client listeners as well as attributes that control event delivery behavior, such asdisabled
orpartialSubmit
.
Because there is no client-side support for EL in the ADF Faces framework, nor is there support for sending entire table models to the client, the client-side code cannot rely on component stamping to access the value. Instead of reusing the same component instance on each row, a new JavaScript client component is created on each row (assuming any component must be created at all for any of the rows).
Therefore, to access row-specific data on the client, you need to use the stamped component itself to access the value. To do this without a client-side data model, you use a client-side selection change listener. For detailed instructions, see Accessing Selected Values on the Client from Collection-Based Components.
Geometry Management for the Table, Tree, and Tree Table Components
By default, when tables, trees, and tree tables are placed in a component that stretches its children (for example, a panelCollection
component inside a panelStretchLayout
component), the table, tree, or tree table will stretch to fill the existing space. However, in order for the columns to stretch to fit the table, you must specify a specific column to stretch to fill up any unused space, using the columnStretching
attribute. Otherwise, the table will only stretch vertically to fit as many rows as possible. It will not stretch the columns, as shown in Figure 12-13.
Figure 12-13 Table Stretches But Columns Do Not
Description of "Figure 12-13 Table Stretches But Columns Do Not"
When placed in a component that does not stretch its children (for example, in a panelCollection
component inside a panelGroupLayout
component set to vertical
), by default, a table width is set to 300px (27.27em units which translates to 300px for an 11px font setting) and the default fetch size is set to return 25 rows, as shown in Figure 12-14.
When you place a table in a component that does not stretch its children, you can control the height of the table so that is never more than a specified number of rows, using the autoHeightRows
attribute. When you set this attribute to a positive integer, the table height will be determined by the number of rows set. If that number is higher than the fetchSize
attribute, then only the number of rows in the fetchSize
attribute will be returned. You can set autoHeightRows
to -1 (the default), to turn off auto-sizing.
Auto-sizing can be helpful in cases where you want to use the same table both in components that stretch their children and those that don't. For example, say you have a table that has 6 columns and can potentially display 12 rows. When you use it in a component that stretches its children, you want the table to stretch to fill the available space. If you want to use that table in a component that doesn't stretch its children, you want to be able to "fix" the height of the table. However, if you set a height on the table, then that table will not stretch when placed in the other component. To solve this issue, you can set the autoHeightRows
attribute, which will be ignored when in a component that stretches, and will be honored in one that does not.
Note:
The default value for the autoHeightRows
attribute is handled by the DEFAULT_DIMENSIONS
web.xml parameter. If you always want table components to be stretched when the parent can stretch, and to be the size of the fetchSize
attribute when it cannot, set the DEFAULT_DIMENSIONS parameter instead of the autoHeightRows
attribute. Set the autoHeightRows
attribute when you want to override the global setting.
By default, DEFAULT_DIMENSIONS is set so that the value of autoHeightRows
is -1
(the table will not stretch). See Geometry Management for Layout and Table Components.
On the other hand, when you do not want to set the autoHeightRows
attribute but still want the table to stretch to fill up the empty vertical space, you can wrap the table with the panelStretchLayout
component and use the viewportBottom
attribute. However, you must explicitly use the AFStretchWidth styleClass
and dimensionsFrom
attributes with the viewPortBottom
attribute. When you use this setup, the distance from the bottom of the panelStretchLayout
to the bottom of the viewport matches the number of the pixels in the viewportBottom
attribute, so that the table resizes to consume that empty space.
This setting is however not recommended as a long-term solution because the user will see the adjustment that takes place when the table is resized after the table displays at an initial size.
The following example shows the panelStretchLayout
component with viewportBottom
attribute code.
<af:panelGroupLayout id="scroller" layout="scroll"> <af:panelStretchLayout id="wrapper" styleClass="AFStretchWidth" <dimensionsFrom="parent" viewportBottom="64"> <f:facet name="center"> <af:table ...>...</af:table> </f:facet> </af:panelStretchLayout> </af:panelGroupLayout>
Displaying Data in Tables
The ADF tables with collection-based components can be formatted using various visual aids. Column data can be stored and sorted. Only partial page rendering happens when the page containing tables is requested by a client.
The immediate children of a table component must be column
components. Each visible column component is displayed as a separate column in the table. Column components contain components used to display content, images, or provide further functionality. For information about the features available with the column component, see Columns and Column 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, the table uses stamping to render each row. Each child is stamped once per row, repeatedly for all the rows. As each row is stamped, the data for the current row is copied into a property that can be addressed using an EL expression. You specify the name to use for this property using the var
property on the table. Once the table has completed rendering, this property is removed or reverted back to its previous value.
Because of this stamping behavior, some components may not work inside the column. Most components will work without problems, for example any input and output components. If you need to use multiple components inside a cell, you can wrap them inside a panelGroupLayout
component. Components that themselves support stamping are not supported, such as tables within a table. For information about using components whose values are determined dynamically at runtime, see What You May Need to Know About Dynamically Determining Values for Selection Components in Tables.
You can use the detailStamp
facet in a table to include data that can be optionally displayed or hidden. When you add a component to this facet, the table displays an additional column with an expand and collapse icon for each row. When the user clicks the icon to expand, the component added to the facet is displayed, as shown in Figure 12-15.
Figure 12-15 Extra Data Can Be Optionally Displayed
When the user clicks on the expanded icon to collapse it, the component is hidden, as shown in Figure 12-16.
Figure 12-16 Extra Data Can Be Hidden
For information about using the detailStamp
facet, see Adding Hidden Capabilities to a Table.
Columns and Column Data
Columns contain the components used to display the data. As stated previously, only one child component is needed for each item to be displayed; the values are stamped as the table renders. Columns can be sorted, and you can configure whether the sort should be case-sensitive or case-insensitive. By default, it is case-sensitive.
Columns can also contain a filtering element. Users can enter a value into the filter and the returned data set will match the value entered in the filter. You can set the filter to be either case-sensitive or case-insensitive. If the table is configured to allow it, users can also reorder columns.
Columns have both header and footer facets. The header facet can be used instead of using the header text attribute of the column, allowing you to use a component that can be styled. The footer facet is displayed at the bottom of the column. For example, Figure 12-17 uses footer facets to display the total at the bottom of two columns. If the number of rows returned is more than can be displayed, the footer facet is still displayed; the user can scroll to the bottom row.
Figure 12-17 Footer Facets in a Column
Formatting Tables
A table component offers many formatting and visual aids to the user. You can enable these features and specify how they can be displayed. These features include:
-
Row selection: By default, at runtime, users cannot select rows. If you want users to be able to select rows in order to perform some action on them somewhere else on the page, or on another page, then enable row selection for the table by setting the
rowSelection
attribute. You can configure the table to allow either a single row or multiple rows to be selected. For information about how to then programmatically perform some action on the selected rows, see What You May Need to Know About Performing an Action on Selected Rows in Tables. -
Scrolling/Pagination: By default, on desktop devices, tables render a scroll bar that allows the user to scroll through all rows. On tablet devices, instead of a scroll bar, the table is paginated and displays a footer that allows the user to jump to specific pages of rows. You can change the default by setting the
scrollPolicy
attribute. -
Table height: You can set the table height to be absolute (for example, 300 pixels), or you can determine the height of the table based on the number of rows you wish to display at a time by setting the
autoHeightRows
attribute. See Geometry Management for the Table, Tree, and Tree Table Components.Note:
When table is placed in a layout-managing container, such as a
panelSplitter
component, it will be sized by the container and theautoHeightRows
is not honored.Note:
You cannot use JavaScript to dynamically size a table. The height of a table is set the first time is rendered and cannot be changed using JavaScript APIs.
-
Grid lines: By default, an ADF table component draws both horizontal and vertical grid lines. These may be independently turned off using the
horizontalGridVisible
andverticalGridVisible
attributes. -
Banding: Groups of rows or columns are displayed with alternating background colors using the
columnBandingInterval
attribute. This helps to differentiate between adjacent groups of rows or columns. By default, banding is turned off. -
Column groups: Columns in a table can be grouped into column groups, by nesting column components. Each group can have its own column group heading, linking all the columns together. See Step 14 in How to Display a Table on a Page.
-
Editable cells: When you elect to use input text components to display data in a table, you can configure the table so that all cells can be edited, or so that the user must explicitly click in the cell in order to edit it. See Editing Data in Tables, Trees, and Tree Tables.
Performance Tip:
When you choose to have cells be available for editing only when the user clicks on them, the table will initially load faster. This may be desirable if you expect the table to display large amounts of data.
-
Column stretching: If the widths of the columns do not together fill the whole table, you can set the
columnStretching
attribute to determine whether or not to stretch columns to fill up the space, and if so, which columns should stretch. You can set the minimum width for columns, so that when there are many columns in a table and you enable stretching, columns will not be made smaller than the set minimum width. You can also set a width percentage for each column you want to stretch to determine the amount of space that column should take up when stretched.Note:
If the total sum of the columns' minimum widths equals more than the viewable space in the viewport, the table will expand outside the viewport and a scrollbar will appear to allow access outside the viewport.
Performance Tip:
Column stretching is turned off by default. Turning on this feature may have a performance impact on the client rendering time when used for complex tables (that is, tables with a large amount of data, or with nested columns, and so on).
Note:
Columns configured to be row headers or configured to be frozen will not be stretched because doing so could easily leave the user unable to access the scrollable body of the table.
-
Column selection: You can choose to allow users to be able to select columns of data. As with row selection, you can configure the table to allow single or multiple column selection. You can also use the
columnSelectionListener
to respond to theColumnSelectionEvent
that is invoked when a new column is selected by the user. This event reports which columns were just deselected and which columns were just selected. -
Column reordering: Users can reorder the columns at runtime by simply dragging and dropping the column headers. By default, column reordering is allowed, and is handled by a menu item in the
panelCollection
component. See Displaying Table Menus, Toolbars, and Status Bars. -
Column freezing: You can configure the table so that columns can be frozen and so will not scroll out of view. Columns can be frozen on either the left or right side of the table. This is controlled by the
freezeDirection
attribute on the table. You choose the column to start the freeze using thefrozen
attribute on the column.Performance Tip:
Use of column freezing increases the complexity of tables and can have a negative performance impact.
Formatting Columns
Each column component also offers many formatting and visual aids to the user. You can enable these features and specify how they can be displayed. These features include:
-
Column sorting: Columns can be configured so that the user can sort the contents by a given column, either in ascending or descending order using the
sortable
attribute. A sort icon on a column header lets the user know that the column can be sorted. The sort icon toggles depending on the sort order. Other columns that can be sorted displays the sort icon on the mouse hover. When the user clicks on the icon to sort a previously unsorted column, the column's content is sorted in ascending order. Subsequent clicks on the same header sort the content in the reverse order. For sortable columns, a space is reserved on the right of the column header for the sort icon.By default, sorting is case-sensitive. That is,
abc
would be sorted beforeABC
. You can configure the column so that instead,abc
would be sorted the same asABC
, using thesortStrength
attribute.In order for the table to be able to sort, the underlying data model must also support sorting. See What You May Need to Know About Programmatically Enabling Sorting for Table Columns.
-
Content alignment: You can align the content within the column to either the start, end, left, right, or center using the
align
attribute.Tip:
Use
start
andend
instead ofleft
andright
if your application supports multiple reading directions. -
Column width: The width of a column can be specified as an absolute value in pixels using the
width
attribute. If you configure a column to allow stretching, then you can also set the width as a percentage. -
Column spanning: You can configure a column to span across other columns using the
colSpan
attribute. Normally however, you use an EL expression as the value for the span, to enable only a certain cell in the column to actually span. For example, Figure 12-18 shows a tree table whosecolSpan
value resolves to span all rows to the right, only if the node is a parent node.Figure 12-18 Column Spans Only When the Node is a Parent
Description of "Figure 12-18 Column Spans Only When the Node is a Parent" -
Line wrapping: You can define whether or not the content in a column can wrap over lines, using the
noWrap
attribute. By default, content will not wrap. -
Row headers: You can define the left-most column to be a row header using the
rowHeader
attribute. When you do so, the left-most column is rendered with the same look as the column headers, and will not scroll off the page. Figure 12-19 shows how a table showing departments appears if the first column is configured to be a row header.If you elect to use a row header column and you configure your table to allow row selection, the row header column displays a selection arrow when a users hovers over the row, as shown in Figure 12-20.
For tables that allow multiple selection, users can mouse down and then drag on the row header to select a contiguous blocks of rows. The table will also autoscroll vertically as the user drags up or down.
In addition, when an error occurs that results in a message for a component in the row, the icon for the severity is displayed in the row header. If more than one message exists, the icon for the maximum severity is displayed. Figure 12-21 shows the error icon displayed in the row header because a date was entered incorrectly.
Figure 12-21 Row Headers Display Message Icons
Description of "Figure 12-21 Row Headers Display Message Icons"
Performance Tip:
Use of row headers increases the complexity of tables and can have a negative performance impact.
Tip:
While the user can change the way the table displays at runtime (for example the user can reorder columns or change column widths), those values will not be retained once the user leaves the page unless you configure your application to allow user customization. For information, see Allowing User Customization on JSF Pages.
How to Display a Table on a Page
You use the Create an ADF Faces Table dialog to add a table to a JSF page. You also use this dialog to add column
components for each column you need for the table. You can also bind the table to the underlying model or bean using EL expressions.
Note:
If your application uses the Fusion technology stack, then you can use data controls to create tables and the binding will be done for you. SeeCreating ADF Databound Tables in Developing Fusion Web Applications with Oracle Application Development Framework.
Once you complete the dialog, and the table and columns are added to the page, you can use the Properties window to configure additional attributes of the table or columns, and add listeners to respond to table events. You must have an implementation of the CollectionModel
class to which your table will be bound.
Before you begin:
It may be helpful to have an understanding of how the attributes can affect functionality. See SeeDisplaying Data in Tables.
You may also find it helpful to understand functionality that can be added using other ADF Faces features. See SeeAdditional Functionality for Collection-Based Components.
To display a table on a page:
-
Create a Java class that extends the
org.apache.myfaces.trinidad.model.CollectionModel
class.Collection components use a
Collection
Model
class to access the data in the underlying collection. This class extends the JSFDataModel
class, but is based on row keys instead of indexes to support underlying data changes. It also supports more advanced functionality, such as sorting.You may also use other model classes, such as
java.util.List
,array
, andjavax.faces.model.DataModel
. If you use one of these other classes, the collection component automatically converts the instance into aCollectionModel
class, but without any additional functionality. For information about theCollectionModel
class, see the MyFaces Trinidad javadoc athttp://myfaces.apache.org/trinidad/trinidad-1_2/trinidad-api/apidocs/index.html
. -
In the Components window, from the Data Views panel, drag and drop a Table onto the page.
-
Use the Create ADF Faces Table dialog to bind the table to any existing model you have. When you bind the table to a valid model, the dialog automatically shows the columns that will be created. You can then use the dialog to edit the values for the columns'
header
andvalue
attributes, and choose the type of component that will be used to display the data. Alternatively, you can manually configure columns and bind at a later date. For help with the dialog, click Help or press F1.Note:
If you are using an
inputText
component to display a Character Large Object (CLOB), then you will need to create a custom converter that converts the CLOB to a String. For information about conversion, see Creating Custom ADF Faces Converters. -
In the Properties window, expand the Common section. If you have already bound your table to a model, the value attribute should be set. You can use this section to set the following table-specific attributes:
-
RowSelection: Set a value to make the rows selectable. Valid values are:
none
,single
, andmultiple
, andmultipleNoSelectAll
.Note:
Users can select all rows and all columns in a table by clicking the column header for the row header if the
rowSelection
attribute is set tomultiple
and that table also contains a row header. If you do not want users to be able to select all columns and rows, then setrowSelection
tomultipleNoSelectAll
.For information about how to then programmatically perform some action on the selected rows, see What You May Need to Know About Performing an Action on Selected Rows in Tables.
-
ColumnSelection: Set a value to make the columns selectable. Valid values are:
none
,single
, andmultiple
.
-
-
Expand the Columns section. If you previously bound your table using the Create ADF Faces Table dialog, then these settings should be complete. You can use this section to change the binding for the table, to change the variable name used to access data for each row, and to change the display label and components used for each column.
Tip:
If you want to use a component other than those listed, select any component in the Properties window, and then manually change it:
-
In the Structure window, right-click the component created by the dialog and choose Convert.
-
Select the desired component from the list. You can then use the Properties window to configure the new component.
Tip:
If you want more than one component to be displayed in a column, add the other component manually and then wrap them both in a
panelGroupLayout
component. To do so:-
In the Structure window, right-click the first component and choose Insert before or Insert after. Select the component to insert.
-
By default the components will be displayed vertically. To have multiple components displayed next to each other in one column, press the shift key and select both components in the Structure window. Right-click the selection and choose Surround With.
-
Select
panelGroupLayout
.
-
-
Expand the Appearance section. You use this section to set the appearance of the table, by setting the following table-specific attributes:
-
Width: Specify the width of the table. You can specify the width as either a number of pixels or as a percentage. The default setting is 300 pixels. If you configure the table to stretch columns (using the
columnStretching
attribute), you must set the width to percentages.Tip:
If the table is a child to a component that stretches its children, then this width setting will be overridden and the table will automatically stretch to fit its container. For information about how components stretch, see Geometry Management for the Table, Tree, and Tree Table Components.
-
ColumnStretching: If the widths of the columns do not together fill the whole table, you can set this attribute to determine whether or not to stretch columns to fill up the space, and if so, which columns should stretch.
Note:
If the table is placed inside a component that can stretch its children, only the table will stretch automatically. You must manually configure column stretching if you want the columns to stretch to fill the table.
Note:
Columns configured to be row headers or configured to be frozen will not be stretched because doing so could easily leave the user unable to access the scrollable body of the table.
Performance Tip:
Column stretching is turned off by default. Turning on this feature may have a performance impact on the client rendering time for complex tables.
You can set column stretching to one of the following values:
-
blank
: If you want to have an empty blank column automatically inserted and have it stretch (so the row background colors will span the entire width of the table). -
A specifically named column: Any column currently in the table can be selected to be the column to stretch.
-
last
: If you want the last column to stretch to fill up any unused space inside of the window. -
none
: The default option where nothing will be stretched. Use this for optimal performance. -
multiple
: All columns that have a percentage value set for theirwidth
attribute will be stretched to that percent, once other columns have been rendered to their (nonstretched) width. The percentage values will be weighted with the total. For example, if you set the width attribute on three columns to 50%, each column will get 1/3 of the remaining space after all other columns have been rendered.
Tip:
While the widths of columns can change at runtime, those width values will not be retained once the user leaves the page unless you configure your application to use change persistence. For information about enabling and using change persistence, see Allowing User Customization on JSF Pages.
-
-
HorizontalGridVisible: Specify whether or not the horizontal grid lines are to be drawn.
-
VerticalGridVisible: Specify whether or not the vertical grid lines are to be drawn.
-
RowBandingInterval: Specify how many consecutive rows form a row group for the purposes of color banding. By default, this is set to 0, which displays all rows with the same background color. Set this to 1 if you want to alternate colors.
-
ColumnBandingInterval: Specify the interval between which the column banding occurs. This value controls the display of the column banding in the table. For example,
columnBandingInterval
=1 would display alternately banded columns in the table. -
FilterVisible: You can add a filter to the table so that it displays only those rows that match the entered filter criteria. If you configure the table to allow filtering, you can set the filter to be case-insensitive or case-sensitive. For information, see Enabling Filtering in Tables.
-
ScrollPolicy: By default, on desktop devices, tables render a scroll bar that allows the user to scroll through all rows. On tablet devices, instead of a scroll bar, tables are rendered to display the table as paginated.
Set the value to
auto
to keep this default behavior. Set the value topage
to have the table always display the rows as sets of pages, with a navigation to those pages in the footer. Set the value toscroll
for tablet devices to have the table always render a scroll bar and scroll with implicit high-water mark scrolling; this setting is particularly useful to address performance problems with virtualized scrolling of table data on tablets. You can specify the number of rows to cache to minimize database roundtrips when the user scrolls back by setting a value for themaxClientRows
attribute.Set the value to
scrollPrefetch
for large tables when there are enough rows left to scroll to ensure smooth scrolling. Prefetching happens in parallel when a user is scrolling. When you set the value of the scrollPolicy attribute toscrollPrefetch
, a table ensures the following:-
When a table is rendered initially, it fetches more rows than that is enough to fill the table viewport depending on the configuration of the table. For example, if the
fetchSize
is 25 and the viewport is 12 rows high, there are 13 rows left to scroll, which is more than half offetchSize
and no additional row beyond the first block is fetched. However, if thefetchSize
is 25 and the viewport is 13 rows high, there are only 12 rows left to scroll, which is less than half offetchSize
and therefore, an additional row will be fetched. -
On subsequent scrolling of the table, the number of client rows left to scroll will be continually monitored and fetching of new data blocks is triggered when the number of rows left to scroll falls below the threshold. However, on tables with known row count, where virtualized scrollbar is supported, fetching will not be initiated until the user releases the mouse button from scrolling.
Note:
For desktop devices, in order to explicitly set a table to display as paginated (configured as the default for tablet devices), you must set the
scrollPolicy
attribute topage
and theautoHeightRows
attribute to0
. If these conditions are not met, the table will display with a scroll bar (whether it is a child to a stretched or a flowing component). For information about container components and tables, see Geometry Management for the Table, Tree, and Tree Table Components. -
-
Text attributes: You can define text strings that will determine the text displayed when no rows can be displayed, as well as a table summary and description for accessibility purposes.
-
-
Expand the Behavior section. You use this section to configure the behavior of the table by setting the following table-specific attributes:
-
ColumnResizing: Specify whether or not you want the end user to be able to resize a column's width at runtime. When set to
disabled
, the widths of the columns will be set once the page is rendered, and the user will not be able to change those widths.Tip:
While the user can change the values of the column width at runtime when
columnResizing
is set totrue
, those width values will not be retained once the user leaves the page unless you configure your application to use change persistence. For information about enabling and using change persistence, see Allowing User Customization on JSF Pages. -
DisableColumnReordering: By default, columns can be reordered at runtime using a menu option contained by default in the
panelCollection
component. You can change this so that users will not be able to change the order of columns. (ThepanelCollection
component provides default menus and toolbar buttons for tables, trees, and tree tables. SeeDisplaying Table Menus, Toolbars, and Status Bars.)Note:
While the user can change the order of columns, those values will not be retained once the user leaves the page unless you configure your application to allow user customization. See Allowing User Customization on JSF Pages.
-
FetchSize: Set the size of the block that should be returned with each data fetch. The default is 25.
Tip:
You should determine the value of the
fetchSize
attribute by taking the height of the table and dividing it by the height of each row to determine how many rows will be needed to fill the table. If thefetchSize
attribute is set too low, it will require multiple trips to the server to fill the table. If it is set too high, the server will need to fetch more rows from the data source than needed, thereby increasing time and memory usage. On the client side, it will take longer to process those rows and attach them to the component. See Content Delivery. For slower database access set higherfetchSize
in order to achieve continuous display and scrolling of data. -
ContentDelivery: Specify when the data should be delivered. When the
contentDelivery
attribute is set toimmediate
, data is fetched at the same time the component is rendered. If thecontentDelivery
attribute is set tolazy
, data will be fetched and delivered to the client during a subsequent request. If the attribute is set towhenAvailable
(the default), the renderer checks if the data is available. If it is, the content is delivered immediately. If it is not, then lazy delivery is used. SeeContent Delivery. -
AutoHeightRows: Specify the number of rows to initially display in the table. When the returned number of rows exceeds this value, a scrollbar is displayed. If you want your table to size to be the same as the
fetchSize
, set it to0
. If you want the table to stretch to fill its parent container that is configured to stretch children, set it to-1
(for information about stretching the table, see Geometry Management for the Table, Tree, and Tree Table Components.). Otherwise set it to a specific number that is lower than the current setting forfetchSize
.Note:
Note the following about setting the
autoHeightRows
attribute:-
Specifying height on the
inlineStyle
attribute will have no effect and will be overridden by the value ofAutoHeightRows
. -
Specifying a
min-height
ormax-height
on theinlineStyle
attribute is not recommended and is incompatible with theautoHeightRows
attribute. -
When the component is placed in a layout-managing container, such as
panelSplitter
, it will be sized by the container (no auto-sizing will occur).
Note:
The default value for the
autoHeightRows
attribute is handled by theDEFAULT_DIMENSIONS
web.xml parameter. If you always want table components to be stretched when the parent can stretch, and to be the size of thefetchSize
attribute when it cannot, set the DEFAULT_DIMENSIONS parameter toauto
, instead of setting theautoHeightRows
attribute.When you set the DEFAULT_DIMENSIONS parameter to
auto
and place the table in a parent that does not stretch its children, and there is no override value for theautoHeightRows
attribute, then the table will take its width from theAFStretchWidth
style class, which by default, will stretch the width of the table to accommodate it's child column components.Set the
autoHeightRows
attribute when you want to override the global setting.By default, DEFAULT_DIMENSIONS is set so that the value of
autoHeightRows
is-1
(the table will not stretch). See Geometry Management for Layout and Table Components. -
-
DisplayRow: Specify the row to be displayed in the table during the initial display. The possible values are
first
to display the first row at the top of the table,last
to display the last row at the bottom of the table (users will need to scroll up to view preceding rows) andselected
to display the first selected row in the table.Note:
The total number of rows from the table model must be known in order for this attribute to work successfully.
-
EditingMode: Specify whether for any editable components, you want all the rows to be editable (
editAll
), you want the user to click a row to make it editable (clickToEdit
), or you want the table to be rendered as read only (readOnly
). For information, see Editing Data in Tables, Trees, and Tree Tables.Tip:
If you choose
clickToEdit
, then only the active row can be edited. This row is determined by theactiveRowKey
attribute. By default, when the table is first rendered, the active row is the first visible row. When a user clicks another row, then that row becomes the active row. You can change this behavior by setting a different value for theactiveRowKey
attribute. -
ContextMenuSelect: Specify whether or not the row is selected when you right-click to open a context menu. When set to
true
, the row is selected. For information about context menus, see Using Popup Dialogs, Menus, and Windows. -
FilterModel: Use in conjunction with
filterVisible
. See Enabling Filtering in Tables. -
Various listeners: Bind listeners to methods that will execute when the table invokes the corresponding event. See Handling Events.
-
-
Expand the Advanced section and set the following table-specific attributes:
-
ActiveRowKey: If you choose
clickToEdit
, then only the active row can be edited. This row is determined by theactiveRowKey
attribute. By default, when the table is first rendered, the active row is the first visible row. When a user clicks another row, then that row becomes the active row. You can change this behavior by setting a different value for theactiveRowKey
attribute. -
DisplayRowKey: Specify the row key to display in the table during initial display. This attribute should be set programmatically rather than declaratively because the value may not be strings. Specifying this attribute will override the
displayRow
attribute.Note:
The total number of rows must be known from the table model in order for this attribute to work successfully.
-
-
Expand the Other section and set the following:
-
BlockRowNavigationOnError: Specify if you want users to be able to navigate away from a row that contains a validation error. When set to
always
, whenever a validation error occurs for a row, the user will always be blocked from navigating to a different row. When set tonever
, the user will never be blocked from navigating to a different row. When set toauto
(the default), the framework will determine if the user can navigate.For example, there may be cases when the table shares its values with another component on the page. You might have a table that allows the user to view a number of different records. When a specific record is selected, its information is displayed in a form. If the user changes some data in the form that causes an error, you do not want the user to then be able to scroll away from that record using the table. So for this example, you might set
BlockRowNavigationOnError
to always. -
FreezeDirection: If you want columns to be able to be frozen, specify whether they should be frozen from the start of the table (the left side in a LTR locale) or the end of the table (the right side in a LTR locale). You must configure the column to start to the freeze using that column's
frozen
attribute.For example, say you want the first three columns to be frozen. On the table, you would set
freezeDirection
tostart
, and on the third column, you would setfrozen
totrue
.If you want the last four columns to be frozen, you would set
freezeDirection
toend
, and on the fourth from last column, you would setfrozen
totrue
-
SelectionEventDelay: Set to
true
if you expect users to navigate through the table using the up and down arrow keys.Users can navigate through the table using a mouse and the scrollbar, or using the up and down arrow keys. By default, a selection event is immediately fired when the user clicks a row. If the user is navigating through the rows using the arrow keys, this means that a selection event will be fired for each row, as the user navigates.
If you expect users to navigate through the table using the keys, you can set the
selectionEventDelay
attribute totrue
, so that there is a 300 millisecond delay before the selection event is fired. If the user navigates to another row within the 300 milliseconds, the selection event is canceled.
-
-
In the Structure window, select a column. In the Properties window, expand the Common section, and set the following column-specific attributes:
-
HeaderText: Specify text to be displayed in the header of the column. This is a convenience that generates output equivalent to adding a header facet containing an
outputText
component. If you want to use a component other thanoutputText
, you should use the column'sheader
facet instead (See Step 16). When theheader
facet is added, any value for theheaderText
attribute will not be rendered in a column header. -
Align: Specify the alignment for this column.
start
,end
, andcenter
are used for left-justified, right-justified, and center-justified respectively in left-to-right display. The valuesleft
orright
can be used when left-justified or right-justified cells are needed, irrespective of the left-to-right or right-to-left display. The default value isnull
, which implies that it is skin-dependent and may vary for the row header column versus the data in the column. For information about skins, see Customizing the Appearance Using Styles and Skins. -
Sortable: Specify whether or not the column can be sorted. A column that can be sorted has a header that when clicked, sorts the table by that column's property. Note that in order for a column to be sortable, the
sortable
attribute must be set totrue
and the underlying model must support sorting by this column's property. See What You May Need to Know About Programmatically Enabling Sorting for Table Columns.Note:
When column selection is enabled, clicking on a column header selects the column instead of sorting the column. In this case, columns can be sorted by clicking the ascending/descending sort indicator.
-
SortStrength: Specify the level of difference to be considered significant when sorting. Choose from one of the following (these values are the same as the values for the Java
Collator
object):-
Primary
: The sorting considers only the letter itself. Case and any accents are ignored:abc
,ÁBC
,ábc
, andABC
will be sorted asabc
,ÁBC
,ábc
,ABC
(the order in which they appear). Use this for case-insensitive sorting. -
Secondary
: The sorting considers the letter and then any accent. Case is ignored:abc
,ÁBC
,ábc
, andABC
will be sorted asabc
,ABC
,ÁBC
,ábc
. In locales that do not have accents, this will result in a case-insensitive search. -
Tertiary
: The sorting will consider the letter, then the accent, and then the case:abc
,ÁBC
,ábc
, andABC
will be sorted asabc
,ABC
,ábc
,ÁBC
. In locales that do not have accents, this will result in a case-sensitive search. -
Identical
: The letters, accents, cases, and any other differences (such as words with punctuation) will be considered:abc
,ab-c
,ÁBC
,ábc
, andABC
will be sorted asabc
,ABC
,ábc
,ÁBC
,ab-c
. This will result in a case-sensitive search, and is the default.
-
-
Filterable: Specify whether or not the column can be filtered. A column that can be filtered has a filter field on the top of the column header. Note that in order for a column to be filterable, this attribute must be set to
true
and thefilterModel
attribute must be set on the table. Only leaf columns can be filtered and the filter component is displayed only if the column header is present. This column'ssortProperty
attribute must be used as a key for thefilterProperty
attribute in thefilterModel
class.Note:
For a column with filtering turned on (
filterable
=true
), you can specify the input component to be used as the filter criteria input field. To do so, add a filter facet to the column and add the input component. See Enabling Filtering in Tables.
-
-
Expand the Appearance section. Use this section to set the appearance of the column, using the following column-specific attributes:
-
DisplayIndex: Specify the display order index of the column. Columns can be rearranged and they are displayed in the table based on the
displayIndex
attribute. Columns without adisplayIndex
attribute value are displayed at the end, in the order in which they appear in the data source. ThedisplayIndex
attribute is honored only for top-level columns, because it is not possible to rearrange a child column outside of the parent column. -
Width: Specify the width of the column. If the table uses column stretching, then you must enter a percentage for the width.
In column stretching, column width percentages are treated as weights. For example, if all columns are given 50% widths, and there are more than three columns, each column will receive an equal amount of space, while still respecting the value set for the
minWidth
attribute.Because the width as a percentage is a weight rather than an actual percentage of space, if column stretching is turned on in the table, and only one column is listed as being stretched by having a percentage width, that column will use up all remaining space in the table not specified by pixel widths in the rest of the columns.
-
MinimumWidth: Specify the minimum number of pixels for the column width. When a user attempts to resize the column, this minimum width will be enforced. Also, when a column is flexible, it will never be stretched to be a size smaller than this minimum width. If a pixel width is defined and if the minimum width is larger, the minimum width will become the smaller of the two values. By default, the minimum width is 10 pixels.
-
ShowRequired: Specify whether or not an asterisk should be displayed in the column header if data is required for the corresponding attribute.
-
HeaderNoWrap and NoWrap: Specify whether or not you want content to wrap in the header and in the column.
-
RowHeader: Set to
true
if you want this column to be a row header for the table.Performance Tip:
Use of row headers increases the complexity of tables and can have a negative performance impact.
-
-
Expand the Behavior section. Use this section to configure the behavior of the columns, using the following column-specific attributes:
-
SortProperty: Specify the property that is to be displayed by this column. This is the property that the framework might use to sort the column's data.
-
Frozen: Specify whether the column is frozen; that is it can't be scrolled off the page. In the table, columns up to the frozen column are locked with the header, and not scrolled with the rest of the columns. The frozen attribute is honored only on the top-level column, because it is not possible to freeze a child column by itself without its parent being frozen.
Note:
By default, columns are frozen from this column to the left. That is, this column and any column to the left of it, will not scroll. You can change this by setting the
freezeDirection
attribute on the table component toend
. By default, it is set tostart
.Performance Tip:
Use of frozen columns increases the complexity of tables and can have a negative performance impact.
-
Selected: When set to
true
, the column will be selected on initial rendering.
-
-
If you want this column to span over subsequent columns, expand the Other section and set ColSpan. You can set it to the number of columns you want it to span, or you can set it to ALL to span to the end of the table. If you don't want all cells in the column to span, you can use an EL expression that resolves to a specific cell or cells.
The following example shows how you might set colSpan in a tree table component where you want only the parent node to span across all columns.
<af:column id="c1" sortable="true" sortProperty="Dname" colSpan="#{testBean.container ? 'ALL' : '1'}" headerText="DepartmentName"> <af:outputText value="#{node.Dname}" id="ot2"/> </af:column>
The following example shows the corresponding managed bean code.
public class TestBean { public boolean isContainer() { return _treeTable.isContainer(); } }
-
To add a column to an existing table, in the Structure window, right-click the table and choose Insert Inside Table > Column. To create column groups, drag a column component and drop it as a child to the component that will be the header. Continue to add columns to create the group. Set the
headerText
attribute on the parent column. -
To add facets to the table, right-click the table and choose Facets - Table and choose the type of facet you want to add. You can then add a component directly to the facet.
Tip:
Because facets on a JSP or JSPX accept one child component only, if you want to add more than one child component, you must wrap the child components inside a container, such as a panelGroupLayout or group component. Facets on a Facelets page can accept more than one component.
-
To add facets to a column, right-click the column and choose Facets - Column, and choose the type of facet you want to add. You can then add a component directly to the facet.
Tip:
Because facets on a JSP or JSPX accept one child component only, if you want to add more than one child component, you must wrap the child components inside a container, such as a panelGroupLayout or group component. Facets on a Facelets page can accept more than one component.
-
Add components as children to the columns to display your data.
The component's value should be bound to the variable value set on the table's
var
attribute and the attribute to be displayed. For example, the table in the File Explorer application usesfile
as the value for thevar
attribute, and the first column displays the name of the file for each row. Therefore, the value of the output component used to display the directory name is#{file.name}
.Tip:
If an input component is the direct child of a column, be sure its width is set to a width that is appropriate for the width of the column. If the width is set too large for its parent column, the browser may extend its text input cursor too wide and cover adjacent columns. For example, if an
inputText
component has its size set to 80 pixels and its parent column size is set to 20 pixels, the table may have an input cursor that covers the clickable areas of it neighbor columns.To allow the input component to be automatically sized when it is not the direct child of a column, set
contentStyle="width:auto"
.
What Happens When You Add a Table to a Page
When you use JDeveloper to add a table onto a page, JDeveloper creates a table with a column for each attribute. If you bind the table to a model, the columns will reflect the attributes in the model. If you are not yet binding to model, JDeveloper will create the columns using the default values. You can change the default values (add/delete columns, change column headings, and so on) during in the table creation dialog or later using the Properties window.
The following example shows abbreviated page code for the table in the File Explorer application.
<af:table id="folderTable" var="file" value="#{explorer.contentViewManager. tableContentView.contentModel}" binding="#{explorer.contentViewManager. tableContentView.contentTable}" emptyText="#{explorerBundle['global.no_row']}" rowselection="multiple" contextMenuId=":context1" contentDelivery="immediate" columnStretching="last" selectionListener="#{explorer.contentViewManager. tableContentView.tableFileItem}" summary="table data"> <af:column width="180" sortable="true" sortStrength="identical" sortProperty="name" headerText="" align="start"> <f:facet name="header"> <af:outputText value="#{explorerBundle['contents.name']}"/> </f:facet> <af:panelGroupLayout> <af:image source="#{file.icon}" inlineStyle="margin-right:3px; vertical-align:middle;" shortDesc="file icon"/> <af:outputText value="#{file.name}" noWrap="true"/> </af:panelGroupLayout> </af:column> <af:column width="70" sortable="true" sortStrength="identical" sortProperty="property.size"> <f:facet name="header"> <af:outputText value="#{explorerBundle['contents.size']}"/> </f:facet> <af:outputText value="#{file.property.size}" noWrap="true"/> </af:column> ... <af:column width="100"> <f:facet name="header"> <af:outputText value="#{explorerBundle['global.properties']}"/> </f:facet> <af:link text="#{explorerBundle['global.properties']}" partialSubmit="true" action="#{explorer.launchProperties}" returnListener="#{explorer.returnFromProperties}" windowWidth="300" windowHeight="300" useWindow="true"> </af:link> </af:column> </af:table>
What Happens at Runtime: Data Delivery
When a page is requested that contains a table, and the content delivery is set to lazy
, the page initially goes through the standard lifecycle. However, instead of fetching the data during that request, a special separate PPR request is run. Because the page has just rendered, only the Render Response phase executes, and the corresponding data is fetched and displayed. If the user's actions cause a subsequent data fetch (for example scrolling in a table), another PPR request is executed. Figure 12-22 shows a page containing a table during the second PPR request. A message is displayed to let the user know that the data is being fetched.
Figure 12-22 Table Fetches Data in a Second PPR Request
Description of "Figure 12-22 Table Fetches Data in a Second PPR Request"
When the user clicks a sortable column header, the table
component generates a SortEvent
event. This event has a getSortCriteria
property, which returns the criteria by which the table must be sorted, along with the sort strength. The table responds to this event by calling the setSortCriteria()
method on the underlying CollectionModel
instance, and calls any registered SortListener
instances.
What You May Need to Know About Programmatically Enabling Sorting for Table Columns
Sorting can be enabled for a table column only if the underlying model supports sorting. If the model is a CollectionModel
instance, it must implement the following methods:
-
public boolean isSortable(String
propertyName
)
-
public List getSortCriteria()
-
public void setSortCriteria(List
criteria
)
The criteria in the second and third methods is a list where each item in the list is an instance of org.apache.myfaces.trinidad.model.SortCriterion
, which supports sort strength.
See the MyFaces Trinidad website at http://myfaces.apache.org/trinidad/index.html
.
If the model is not a CollectionModel
instance, the table component wraps that model into an org.apache.myfaces.trinidad.model.SortableModel
instance and converts the model to a CollectionModel
instance that is sortable (SortableModel
is a concrete class that extends CollectionModel
and implements sorting functionality). In this case, the table will examine the actual data to determine which properties are sortable. Any column that has data that implements java.lang.Comparable
will be sortable. This automatic support for sorting by the table is not as efficient as sorting directly into a CollectionModel
instance but is sufficient for small data sets. Note that tables with a converted model allow sorting for only one column and therefore multi-column table sorting (normally done by supplying multiple sort criteria) is not supported on the converted model.
Note:
When the underlying table model is not a CollectionModel
instance and multi-column sorting is desired, consider using the table inside a panelCollection
component. The panel user interface allows the user to sort using multiple sort criteria even though automatic sorting provided by the table with a converted model does not support it. For details about the panelCollection
component, see How to Add a panelCollection with a Table, Tree, or Tree Table.
What You May Need to Know About Performing an Action on Selected Rows in Tables
A collection-based component can allow users to select one or more rows and perform some actions on those rows (the carousel component does not support multiple selection).
When the selection state of a component changes, the component triggers selection events. A selectionEvent
event reports which rows were just deselected and which rows were just selected.
To listen for selection events on a component, you can register a listener on the component either using the selectionListener
attribute or by adding a listener to the component using the addselectionListener()
method. The listener can then access the selected rows and perform some actions on them.
The current selection, that is the selected row or rows, are the RowKeySet
object, which you obtain by calling the getSelectedRowKeys()
method for the component. To change a selection programmatically, you can do either of the following:
-
Add
rowKey
objects to, or removerowKey
objects from, theRowKeySet
object. -
Make a particular row current by calling the
setRowIndex()
or thesetRowKey()
method on the component. You can then either add that row to the selection, or remove it from the selection, by calling theadd()
orremove()
method on theRowKeySet
object.
The following example shows a portion of a table in which a user can select some rows then click the Delete button to delete those rows. Note that the actions listener is bound to the performDelete
method on the mybean
managed bean.
<af:table binding="#{mybean.table}" rowselection="multiple" ...> ... </af:table> <af:button text="Delete" actionListener="#{mybean.performDelete}"/>
The following example shows an actions method, performDelete
, which 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(); try { while(selection.hasNext()) { Object rowKey = selection.next(); table.setRowKey(rowKey); MyRowImpl row = (MyRowImpl) table.getRowData(); //custom method exposed on an implementation of Row interface. row.markForDeletion(); } } finally { // restore the old key: table.setRowKey(oldKey); } }
Note:
When usingsetRowKey
and setRowIndex
on table/tree/treeTable to change the current record for operation, you must restore the old current record after the operation. Otherwise, failure to restore the old currency can cause application errors.
What You May Need to Know About Dynamically Determining Values for Selection Components in Tables
There may be a case when you want to use a selectOne
component in a table, but you need each row to display different choices in a component. Therefore, you need to dynamically determine the list of items at runtime.
While you may think you should use a forEach
component to stamp out the individual items, this will not work because forEach
does not work with the CollectionModel
instance. It also cannot be bound to EL expressions that use component-managed EL variables, as those used in the table. The forEach
component performs its functions in the JSF tag execution step while the table performs in the following component encoding step. Therefore, the forEach
component will execute before the table is ready and will not perform its iteration function.
In the case of a selectOne
component, the direct child must be the items
component. While you could bind the items
component directly to the row variable (for example, <f:items value="#{row.Items}"/>
, doing so would not allow any changes to the underlying model.
Instead, you should create a managed bean that creates a list of items, as shown in the following example.
public List<SelectItem> getItems() { // Grab the list of items FacesContext context = FacesContext.getCurrentInstance(); Object rowItemObj = context.getApplication().evaluateExpressionGet( context, "#{row.items}", Object.class); if (rowItemObj == null) return null; // Convert the model objects into items List<SomeModelObject> list = (List<SomeModelObject>) rowItemObj; List<SelectItem> items = new ArrayList<SelectItem>(list.size()); for (SomeModelObject entry : list) { items.add(new SelectItem(entry.getValue(), entry.getLabel());public } // Return the items return items; }
You can then access the list from the one component on the page:
<af:table var="row"> <af:column> <af:selectOneChoice value="#{row.myValue}"> <f:Items value="#{page_backing.Items}"/> </af:selectOneChoice> </af:column> </af:table>
What You May Need to Know About Read Only Tables
A table can be set as read-only by setting the editingMode
attribute to readOnly
value. If the editingMode
attribute is set to clickToEdit
type, then the table will result in a regular clickToEdit
behavior.
A table can be rendered as editable, read-only, or edit-upon-clicking a row or a node type by setting an appropriate value on the editingMode
attribute. When the editingMode
attribute is set to readyOnly
value, all the components of that table will be rendered as read-only, including input components. If editingMode
is set to clickToEdit
value, then that table will result in a regular clickToEdit
behavior.
Setting the web.xml
parameter oracle.adf.view.rich.table.clickToEdit.initialRender.readOnly
will force all clickToEdit
type tables in an application to render as read-only only when the tables are initially loaded. All tables using other editingMode
settings will be unaffected. See Rendering Tables Initially as Read Only.
The var
status of the table is updated with a new variable readOnly
(varStatus.readOnly
). This will provide information about the current status of the row if it is read-only or not. Applications can use this variable along with other conditional operators to decide which component to render. Following is example code using varStatus.readOnly
to render an input text for an editable row and output text for a read-only row.
-
You can add
varStatus
attribute in your table as follows:<af:table value="#{bindings.EmpView1.collectionModel}" var="row" rows="#{bindings.EmpView1.rangeSize}" emptyText="#{bindings.EmpView1.viewable ? 'No data to display.' : 'Access Denied.'}" fetchSize="#{bindings.EmpView1.rangeSize}" rowBandingInterval="0" id="t1" inlineStyle="width:1000px;" editingMode="clickToEdit" varStatus="vars" binding="#{backingBeanScope.Backing.t1}" partialTriggers="::cb4 ::cb5 ::cb6 ::cb7 deptno1Id" columnSelection="single">
-
You can use
varStatus
attribute in your column definition as follows:<af:column headerText="switch" id="c100" width="130px"> <af:inputText value="#{row.bindings.Hiredate.inputValue}" required="#{bindings.EmpView1.hints.Hiredate.mandatory}" columns="#{bindings.EmpView1.hints.Hiredate.displayWidth}" maximumLength="#{bindings.EmpView1.hints.Hiredate.precision}" shortDesc="#{bindings.EmpView1.hints.Hiredate.tooltip}" id="it2" rendered="#{!vars.readOnly}"> <af:validator binding="#{row.bindings.Hiredate.validator}"/> <af:convertDateTime pattern="#{bindings.EmpView1.hints.Hiredate.format}"/> </af:inputText> <af:outputText value="outputtext #{row.Hiredate}" id="ot2" rendered="#{vars.readOnly}"> <af:convertDateTime pattern="#{bindings.EmpView1.hints.Hiredate.format}"/> </af:outputText> </af:column>
Adding Hidden Capabilities to a Table
While displaying the content in an ADF table built with collection-based components you can hide or display some content using detailStamp facet. The content appears with a toggle icon, when user clicks on it the hidden text is displayed in a popup window.
You can use the detailStamp
facet in a table to include data that can be displayed or hidden. When you add a component to this facet, the table displays an additional column with a toggle icon. When the user clicks the icon, the component added to the facet is shown. When the user clicks on the toggle icon again, the component is hidden. Figure 12-23 shows the additional column that is displayed when content is added to the detailStamp
facet.
Figure 12-23 Table with Unexpanded DetailStamp Facet
Figure 12-24 shows the same table, but with the detailStamp
facet expanded for the first row.
Figure 12-24 Expanded detailStamp Facet
You can use an EL expression for the rendered
attribute on the facet to determine whether or not to display the toggle icon and show details. For example, say on a shopping cart page you want to use the detailStamp
facet to display gift wrapping information. However, not all order items will have gift wrapping information, so you only want the toggle icon to display if the order item has the information to display. You could create a method on managed bean that determines if there is information to display, and then bind the rendered
attribute to that method. Figure 12-25 shows the same table but with icons displayed only for the rows that have information to display.
Figure 12-25 Conditional detailStamp Facet
Note:
If you set the table to allow columns to freeze, the freeze will not work when you display the detailStamp
facet. That is, a user cannot freeze a column while the details are being displayed.
How to Use the detailStamp Facet
To use the detailStamp
facet, you insert a component that is bound to the data to be displayed or hidden into the facet.
Before you begin:
It may be helpful to have an understanding of how the attributes can affect functionality. See Adding Hidden Capabilities to a Table.
You may also find it helpful to understand functionality that can be added using other ADF Faces features. See Additional Functionality for Collection-Based Components.
To use the detailStamp facet:
Note:
If your application uses the Fusion technology stack, then you can drag attributes from a data control and drop them into the detailStamp
facet. You don't need to modify the code.
What Happens at Runtime: The rowDisclosureEvent
When the user hides or shows the details of a row, the table generates a rowDisclosureEvent
event. The event tells the table to toggle the details (that is, either expand or collapse).
The rowDisclosureEvent
event has an associated listener. You can bind the rowDisclosureListener
attribute on the table to a method on a managed bean. This method will then be invoked in response to the rowDisclosureEvent
event to execute any needed post-processing.
Enabling Filtering in Tables
An ADF table built with collection-based components can filter data in the table if the filterModel attribute object of the table is bound to an instance of the FilterableQueryDescriptor class.
You can add a filter to a table that can be used so that the table displays only rows whose values match the filter. When enabled and set to visible, a search criteria input field displays above each searchable column.
For example, the table in Figure 12-27 has been filtered to display only rows in which the Location
value is 1700.
Filtered table searches are based on Query-by-Example and use the QBE text or date input field formats. The input validators are turned off to allow for entering characters for operators such as >
and <
to modify the search criteria. For example, you can enter >1500
as the search criteria for a number column. Wildcard characters may also be supported. Searches can be either case-sensitive or case-insensitive. If a column does not support QBE, the search criteria input field will not render for that column.
The filtering feature uses a model for filtering data into the table. The table's filterModel
attribute object must be bound to an instance of the FilterableQueryDescriptor
class.
Note:
If your application uses the Fusion technology stack, then you can use data controls to create tables and filtering will be created for you. See Creating ADF Databound Tables in Developing Web User Interfaces with Oracle ADF Faces
In the following example, the table filterVisible
attribute is set to true
to enable the filter input fields. For each column to be filtered, you must set the sortProperty
attribute to the associated column in the filterModel
instance and the filterable
attribute set to true
.
<af:table value="#{myBean.products}" var="row" ... filterVisible="true" ... rowselection="single"> ... <af:column sortProperty="ProductID" filterable="true" sortable="true" <af:outputText value="#{row.ProductID}"> ... </af:column> <af:column sortProperty="Name" filterable="true" sortable="true" <af:outputText value="#{row.Name}"/> ... </af:column> <af:column sortProperty="warehouse" filterable="true" sortable="true" <af:outputText value="#{row.warehouse}"/> ... </af:column> </af:table>
How to Add Filtering to a Table
To add filtering to a table, first create a class that can provide the filtering functionality. You then bind the table to that class, and configure the table and columns to use filtering. The table that will use filtering must either have a value for its headerText
attribute, or it must contain a component in the header
facet of the column that is to be filtered. This allows the filter component to be displayed. Additionally, the column must be configured to be sortable, because the filterModel
class uses the sortProperty
attribute.
Before you begin:
It may be helpful to have an understanding of how the attributes can affect functionality. See Enabling Filtering in Tables.
You may also find it helpful to understand functionality that can be added using other ADF Faces features. See Additional Functionality for Collection-Based Components.
To add filtering to a table:
-
Create a Java class that is a subclass of the
FilterableQueryDescriptor
class.The
ConjunctionCriterion
object returned from thegetFilterConjunctionCriterion
method must not benull
. For information about this class, see theADF Faces Javadoc
. -
Create a table, as described in Displaying Data in Tables.
-
Select the table in the Structure window and set the following attributes in the Properties window:
-
FilterVisible: Set to
true
to display the filter criteria input field above searchable column. -
FilterModel: Bind to an instance of the
FilterableQueryDescriptor
class created in Step 1.
Tip:
If you want to use a component other than an
inputText
component for your filter (for example, aninputDate
component), then instead of settingfilterVisible
totrue
, you can add the needed component to thefilter
facet. To do so:-
In the Structure window, right-click the column to be filtered and choose Insert inside af:column > JSF Core > Filter facet.
-
From the Components window, drag and drop a component into the facet.
-
Set the value of the component to the corresponding attribute within the
FilterableQueryDescriptor
class created in Step 1. Note that the value must take into account the variable used for the row, for example:#{af:inputDate label="Select Date" id="name" value="row.filterCriteria.date"}
-
-
In the Structure window, select a column in the table and in the Properties window, and set the following for each column in the table:
-
Filterable: Set to
true
. -
FilterFeatures: Set to
caseSensitive
orcaseInsensitive
. If not specified, the case sensitivity is determined by the model.
-
Displaying Data in Trees
Any hierarchical data can be displayed using ADF tree component.
The ADF Faces tree component displays hierarchical data, such as organization charts or hierarchical directory structures. In data of these types, there may be a series of top-level nodes, and each element in the structure may expand to contain other elements. For example, in an organization chart, any number of employees in the hierarchy may have any number of direct reports. The tree component can be used to show that hierarchy, where the direct reports appear as children to the node for the employee.
The tree component supports multiple root elements. It displays the data in a form that represents the structure, with each element indented to the appropriate level to indicate its level in the hierarchy, and connected to its parent. Users can expand and collapse portions of the hierarchy. Figure 12-28 shows a tree used to display directories in the File Explorer application.
Figure 12-28 Tree Component in the File Explorer Application
Description of "Figure 12-28 Tree Component in the File Explorer Application"
The ADF Faces tree component uses a model to access the data in the underlying hierarchy. The specific model class is oracle.adf.view.rich.model.TreeModel
, which extends CollectionModel
, described in Displaying Data in Tables.
You must create your own tree model to support your tree. The tree model is a collection of rows. It has an isContainer()
method that returns true
if the current row contains child rows. To access the children of the current row, you call the enterContainer()
method. Calling this method results in the TreeModel
instance changing to become a collection of the child rows. To revert back up to the parent collection, you call the exitContainer()
method.
You may find the org.apache.myfaces.trinidad.model.ChildPropertyTreeModel
class useful when constructing a TreeModel
class, as shown in the following example.
List<TreeNode> root = new ArrayList<TreeNode>(); for(int i = 0; i < firstLevelSize; i++) { List<TreeNode> level1 = new ArrayList<TreeNode>(); for(int j = 0; j < i; j++) { List<TreeNode> level2 = new ArrayList<TreeNode>(); for(int k=0; k<j; k++) { TreeNode z = new TreeNode(null, _nodeVal(i,j,k)); level2.add(z); } TreeNode c = new TreeNode(level2, _nodeVal(i,j)); level1.add(c); } TreeNode n = new TreeNode(level1, _nodeVal(i)); root.add(n); } ChildPropertyTreeModel model = new ChildPropertyTreeModel(root, "children"); private String _nodeVal(Integer... args) { StringBuilder s = new StringBuilder(); for(Integer i : args) s.append(i); return s.toString(); }
Note:
If your application uses the Fusion technology stack, then you can use data controls to create trees and the model will be created for you. See Displaying Master-Detail Data in Developing Web User Interfaces with Oracle ADF Faces
You can manipulate the tree similar to the way you can manipulate a table. You can do the following:
-
To make a node current, call the
setRowIndex()
method on the tree with the appropriate index into the list. Alternatively, call thesetRowKey()
method with the appropriaterowKey
object. -
To access a particular node, first make that node current, and then call the
getRowData()
method on the tree. -
To access rows for expanded or collapsed nodes, call
getAddedSet
andgetRemovedSet
methods on theRowDisclosureEvent
. See What You May Need to Know About Programmatically Expanding and Collapsing Nodes. -
To manipulate the node's child collection, call the
enterContainer()
method before calling thesetRowIndex()
andsetRowKey()
methods. Then call theexitContainer()
method to return to the parent node. -
To point to a
rowKey
for a node inside the tree (at any level) use thefocusRowKey
attribute. ThefocusRowKey
attribute is set when the user right-clicks on a node and selects the Show as top (or the Show as top toolbar button in thepanelCollection
component).When the
focusRowKey
attribute is set, the tree renders the node pointed to by thefocusRowKey
attribute as the root node in the Tree and displays a Hierarchical Selector icon next to the root node. Clicking the Hierarchical Selector icon displays a Hierarchical Selector dialog which shows the path to thefocusRowKey
object from the root node of the tree. How this displays depends on the components placed in thepathStamp
facet.
Note:
You cannot use JavaScript to dynamically size a tree. The height of a tree is set the first time is rendered and cannot be changed using JavaScript APIs.
As with tables, trees use stamping to display content for the individual nodes. Trees contain a nodeStamp
facet, which is a holder for the component used to display the data for each node. Each node is rendered (stamped) once, repeatedly for all nodes. As each node is stamped, the data for the current node is copied into a property that can be addressed using an EL expression. Specify the name to use for this property using the var
property on the tree. Once the tree has completed rendering, this property is removed or reverted back to its previous value.
Because of this stamping behavior, only certain types of components are supported as children inside an ADF Faces tree. All components that have no behavior are supported, as are most components that implement the ValueHolder
or ActionSource
interfaces.
In the following example, the data for each element is referenced using the variable node
, which identifies the data to be displayed in the tree. The nodeStamp
facet displays the data for each element by getting further properties from the node
variable:
<af:tree var="node"> <f:facet name="nodeStamp"> <af:outputText value="#{node.firstname}"/> </f:facet> </af:tree>
Trees also contain a pathStamp
facet. This facet determines how the content of the Hierarchical Selector dialog is rendered, just like the nodeStamp
facet determines how the content of the tree is rendered. The component inside the pathStamp
facet can be a combination of simple outputText
, image
, and outputFormatted
tags and cannot not be any input component (that is, any EditableValueHolder
component) because no user input is allowed in the Hierarchical Selector popup. If this facet is not provided, then the Hierarchical Selector icon is not rendered.
For example, including an image and an outputText
component in the pathStamp
facet causes the tree to render an image and an outputText
component for each node level in the Hierarchical Selector dialog. Use the same EL expression to access the value. For example, if you want to show the first name for each node in the path in an outputText
component, the EL expression would be <af:outputText value="#{node.firstname}"/>
.
Tip:
The pathStamp
facet is also used to determine how default toolbar buttons provided by the panelCollection
component will behave. If you want to use the buttons, add a component bound to a node value. For information about using the panelCollection
component, see Displaying Table Menus, Toolbars, and Status Bars.
How to Display Data in Trees
To create a tree, you add a tree component to your page and configure the display and behavior properties.
Before you begin:
It may be helpful to have an understanding of how the attributes can affect functionality. See Displaying Data in Trees.
You may also find it helpful to understand functionality that can be added using other ADF Faces features. See Additional Functionality for Collection-Based Components.
To add a tree to a page:
What Happens When You Add a Tree to a Page
When you add a tree to a page, JDeveloper adds a nodeStamp
facet to stamp out the nodes of the tree. The following example shows the abbreviated code for the tree in the File Explorer application that displays the directory structure.
<af:tree id="folderTree" var="folder" binding="#{explorer.navigatorManager.foldersNavigator .foldersTreeComponent}" value="#{explorer.navigatorManager.foldersNavigator. foldersTreeModel}" disclosedRowKeys="#{explorer.navigatorManager.foldersNavigator. foldersTreeDisclosedRowKeys}" rowSelection="single" contextMenuId=":context2" selectionListener="#{explorer.navigatorManager.foldersNavigator. showSelectedFolderContent}"> <f:facet name="nodeStamp"> <af:panelGroupLayout> <af:image id="folderNodeStampImg" source="#{folder.icon}" inlineStyle="vertical-align:middle; margin-right:3px; shortDesc="folder icon"/> <af:outputText id="folderNodeStampText" value="#{folder.name}"/> </af:panelGroupLayout> </f:facet> </af:tree>
What Happens at Runtime: Tree Component Events
The tree is displayed in a format with nodes indented to indicate their levels in the hierarchy. The user can click nodes to expand them to show children nodes. The user can click expanded nodes to collapse them. When a user clicks one of these icons, the component generates a RowDisclosureEvent
event. You can register a custom rowDisclosureListener
method to handle any processing in response to the event. See What You May Need to Know About Programmatically Expanding and Collapsing Nodes.
When a user selects or deselects a node, the tree component invokes a selectionEvent
event. You can register custom selectionListener
instances, which can do post-processing on the tree component based on the selected nodes. See What You May Need to Know About Programmatically Selecting Nodes.
What You May Need to Know About Programmatically Expanding and Collapsing Nodes
The RowDisclosureEvent
event has two RowKeySet
objects: the RemovedSet
object for all the collapsed nodes and the AddedSet
object for all the expanded nodes. The component expands the subtrees under all nodes in the added set and collapses the subtrees under all nodes in the removed set.
Your custom rowDisclosureListener
method can do post-processing, on the tree component, as shown in the following example.
<af:treeTable id="folderTree" var="directory" value="#{fs.treeModel}" binding="#{editor.component}" rowselection="multiple" columnselection="multiple" focusRowKey="#{fs.defaultFocusRowKey}" selectionListener="#{fs.Table}" contextMenuId="treeTableMenu" rowDisclosureListener="#{fs.handleRowDisclosure}">
For the contraction of a tree node, you use getRemovedSet
in a backing bean method that handles row disclosure events, as shown in the following example that illustrates the expansion of a tree node.
public void handleRowDisclosure(RowDisclosureEvent rowDisclosureEvent) throws Exception { Object rowKey = null; Object rowData = null; RichTree tree = (RichTree) rowDisclosureEvent.getSource(); RowKeySet rks = rowDisclosureEvent.getAddedSet(); if (rks != null) { int setSize = rks.size(); if (setSize > 1) { throw new Exception("Unexpected multiple row disclosure added row sets found."); } if (setSize == 0) { // nothing in getAddedSet indicates this is a node // contraction, not expansion. If interested only in handling // node expansion at this point, return. return; } rowKey = rks.iterator().next(); tree.setRowKey(rowKey); rowData = tree.getRowData(); // Do whatever is necessary for accessing tree node from // rowData, by casting it to an appropriate data structure // for example, a Java map or Java bean, and so forth. } }
Trees and tree tables use an instance of the oracle.adf.view.rich.model.RowKeySet
class to keep track of which nodes are expanded. This instance is stored as the disclosedRowKeys
attribute on the component. You can use this instance to control the expand or collapse state of an node in the hierarchy programmatically, as shown in the following example. Any node contained by the RowKeySet
instance is expanded, and all other nodes are collapsed. The addAll()
method adds all elements to the set, and the and removeAll()
method removes all the nodes from the set.
<af:tree var="node" inlineStyle="width:90%; height:300px" id="displayRowTable" varStatus="vs" rowselection="single" disclosedRowKeys="#{treeTableTestData.disclosedRowKeys}" value="#{treeTableTestData.treeModel}">
The backing bean method that handles the disclosed row keys is shown in the following example].
public RowKeySet getDisclosedRowKeys() { if (disclosedRowKeys == null) { // Create the PathSet that we will use to store the initial // expansion state for the tree RowKeySet treeState = new RowKeySetTreeImpl(); // RowKeySet requires access to the TreeModel for currency. TreeModel model = getTreeModel(); treeState.setCollectionModel(model); // Make the model point at the root node int oldIndex = model.getRowIndex(); model.setRowKey(null); for(int i = 1; i<=19; ++i) { model.setRowIndex(i); treeState.setContained(true); } model.setRowIndex(oldIndex); disclosedRowKeys = treeState; } return disclosedRowKeys; }
What You May Need to Know About Programmatically Selecting Nodes
The tree and tree table components allow nodes to be selected, either a single node only, or multiple nodes. If the component allows multiple selections, users can select multiple nodes using Control+click and Shift+click operations.
When a user selects or deselects a node, the tree component fires a selectionEvent
event. This event has two RowKeySet
objects: the RemovedSet
object for all the deselected nodes and the AddedSet
object for all the selected nodes.
Tree and tree table components keep track of which nodes are selected using an instance of the class oracle.adf.view.rich.model.RowKeySet
. This instance is stored as the selectedRowKeys
attribute on the component. You can use this instance to control the selection state of a node in the hierarchy programmatically. Any node contained by the RowKeySet
instance is deemed selected, and all other nodes are not selected. The addAll()
method adds all nodes to the set, and the and removeAll()
method removes all the nodes from the set. Tree and tree table node selection works in the same way as table row selection. You can refer to sample code for table row selection in What You May Need to Know About Performing an Action on Selected Rows in Tables.
Displaying Data in Tree Tables
The ADF Tree Table component can display content in a hierarchical structure. Using this component you can display the content more elaborately than by using a tree component.
The ADF Faces tree table component displays hierarchical data in the form of a table. The display is more elaborate than the display of a tree component, because the tree table component can display columns of data for each tree node in the hierarchy. The component includes mechanisms for focusing on subtrees within the main tree, as well as expanding and collapsing nodes in the hierarchy. Figure 12-30 shows the tree table used in the File Explorer application. Like the tree component, the tree table can display the hierarchical relationship between the files in the collection. And like the table component, it can also display attribute values for each file.
Figure 12-30 Tree Table in the File Explorer Application
Description of "Figure 12-30 Tree Table in the File Explorer Application"
The immediate children of a tree table component must be column components, in the same way as for table components. Unlike the table, the tree table component has a nodeStamp
facet which holds the column that contains the primary identifier of an node in the hierarchy. The treeTable
component supports the same stamping behavior as the Tree
component (see Displaying Data in Trees).
Note:
The nodeStamp
facet can only contain one column (which becomes the node in the tree).
For example, in the File Explorer application (as shown in Figure 12-30), the primary identifier is the file name. This column is what is contained in the nodeStamp
facet. The other columns, such as Type and Size, display attribute values on the primary identifier, and these columns are the direct children of the tree table component. This tree table uses node
as the value of the variable that will be used to stamp out the data for each node in the nodeStamp
facet column and each component in the child columns. The following example shows abbreviated code for the tree table in the File Explorer application.
<af:treeTable id="folderTreeTable" var="file"
value="#{explorer.contentViewManager.treeTableContentView.
contentModel}"
binding="#{explorer.contentViewManager.treeTableContentView.
contentTreeTable}"
emptyText="#{explorerBundle['global.no_row']}"
columnStretching="last"
rowSelection="single"
selectionListener="#{explorer.contentViewManager.
treeTableContentView.treeTableSelectFileItem}"
summary="treeTable data">
<f:facet name="nodeStamp">
<af:column headerText="#{explorerBundle['contents.name']}"
width="200" sortable="true" sortProperty="name">
<af:panelGroupLayout>
<af:image source="#{file.icon}"
shortDesc="#{file.name}"
inlineStyle="margin-right:3px; vertical-align:middle;"/>
<af:outputText id="nameStamp" value="#{file.name}"/>
</af:panelGroupLayout>
</af:column>
</f:facet>
<f:facet name="pathStamp">
<af:panelGroupLayout>
<af:image source="#{file.icon}"
shortDesc="#{file.name}"
inlineStyle="margin-right:3px; vertical-align:middle;"/>
<af:outputText value="#{file.name}"/>
</af:panelGroupLayout>
</f:facet>
<af:column headerText="#{explorerBundle['contents.type']}">
<af:outputText id="typeStamp" value="#{file.type}"/>
</af:column>
<af:column headerText="#{explorerBundle['contents.size']}">
<af:outputText id="sizeStamp" value="#{file.property.size}"/>
</af:column>
<af:column headerText="#{explorerBundle['contents.lastmodified']}"
width="140">
<af:outputText id="modifiedStamp"
value="#{file.property.lastModified}"/>
</af:column>
</af:treeTable>
The tree table component supports many of the same attributes as both tables and trees. For additional information about these attributes see Displaying Data in Tables and Displaying Data in Trees.
How to Display Data in a Tree Table
You use the Insert Tree Table wizard to create a tree table. Once the wizard is complete, you can use the Properties window to configure additional attributes on the tree table.
Before you begin:
It may be helpful to have an understanding of how the attributes can affect functionality. See Displaying Data in Tree Tables.
You may also find it helpful to understand functionality that can be added using other ADF Faces features. See Additional Functionality for Collection-Based Components.
To add a tree table to a page:
Passing a Row as a Value
The ADF allows you to pass the data of an entire row of a table or a tree residing on another page within the framework. You can achieve this by using the setPropertyListener tag. This is helpful when you want to retrieve the data in a table by a click of a button.
There may be a case where you need to pass an entire row from a collection as a value. To do this, you pass the variable used in the table to represent the row, or used in the tree to represent a node, and pass it as a value to a property in the pageFlow
scope. Another page can then access that value from the scope. The setPropertyListener
tag allows you to do this (for more information about the setPropertyListener
tag, including procedures for using it, see Passing Values Between Pages).
For example, suppose you have a master page with a single-selection table showing employees, and you want users to be able to select a row and then click a button to navigate to a new page to edit the data for that row, as shown in the following example. The EL variable name emp
is used to represent one row (employee) in the table. The action
attribute value of the button
component is a static string outcome showEmpDetail
, which allows the user to navigate to the Employee Detail page. The setPropertyListener
tag takes the from
value (the variable emp
), and stores it with the to
value.
<af:table value="#{myManagedBean.allEmployees}" var="emp" rowSelection="single"> <af:column headerText="Name"> <af:outputText value="#{emp.name}"/> </af:column> <af:column headerText="Department Number"> <af:outputText value="#{emp.deptno}"/> </af:column> <af:column headertext="Select"> <af:button text="Show more details" action="showEmpDetail"> <af:setPropertyListener from="#{emp}" to="#{pageFlowScope.empDetail}" type="action"/> </af:button> </af:column> </af:table>
When the user clicks the button on an employee row, the listener executes, and the value of #{emp}
is retrieved, which corresponds to the current row (employee) in the table. The retrieved row object is stored as the empDetail
property of pageFlowScope
with the #{pageFlowScope.empDetail}
EL expression. Then the action event executes with the static outcome, and the user is navigated to a detail page. On the detail page, the outputText
components get their value from pageFlowScope.empDetail
objects, as shown in the following example.
<h:panelGrid columns="2"> <af:outputText value="Firstname:"/> <af:inputText value="#{pageFlowScope.empDetail.name}"/> <af:outputText value="Email:"/> <af:inputText value="#{pageFlowScope.empDetail.email}"/> <af:outputText value="Hiredate:"/> <af:inputText value="#{pageFlowScope.empDetail.hiredate}"/> <af:outputText value="Salary:"/> <af:inputText value="#{pageFlowScope.empDetail.salary}"/> </h:panelGrid>
Displaying Table Menus, Toolbars, and Status Bars
Using ADF panelCollection component you can add menus, toolbars, and status bars to a table. You can also add actions, such as expanding or collapsing the content, detaching the toolbar from the tree or tree table, and so on.
You can use the panelCollection
component to add menus, toolbars, and status bars to tables, trees, and tree tables. To use the panelCollection
component, you add the table, tree, or tree table component as a direct child of the panelCollection
component. The panelCollection
component provides default menus and toolbar buttons.
Figure 12-31 shows the panelCollection
component with the tree table component in the File Explorer application. The toolbar contains a menu that provides actions that can be performed on the tree table (such as expanding and collapsing nodes), a button that allows users to detach the tree table, and buttons that allow users to change the rows displayed in the tree table. You can configure the toolbar to not display certain toolbar items. For example, you can turn off the buttons that allow the user to detach the tree or table. For information about menus, toolbars, and toolbar buttons, see Using Menus, Toolbars, and Toolboxes.
Figure 12-31 Panel Collection for Tree Table with Menus and Toolbar
Description of "Figure 12-31 Panel Collection for Tree Table with Menus and Toolbar"
Among other facets, the panelCollection
component contains a menu
facet to hold menu components, a toolbar
facet for toolbar components, a secondaryToolbar
facet for another set of toolbar components, and a statusbar
facet for status items.
The default top-level menu and toolbar items vary depending on the component used as the child of the panelCollection
component:
-
Table and tree: Default top-level menu is View.
-
Table and tree table with selectable columns: Default top-level menu items are View and Format.
-
Table and tree table: Default toolbar menu is Detach.
-
Table and tree table with selectable columns: Default top-level toolbar items are Freeze, Detach, and Wrap
-
Tree and tree table (when the
pathStamp
facet is used): The toolbar buttons Go Up, Go To Top, and Show as Top also appear.
The following example shows how the panelCollection
component contains menus and toolbars.
<af:panelCollection binding="#{editor.component}"> <f:facet name="viewMenu"> <af:group> <af:commandMenuItem text="View Item 1..."/> <af:commandMenuItem text="View Item 2.."/> <af:commandMenuItem text="View Item 3..." disabled="true"/> <af:commandMenuItem text="View Item 4"/> </af:group> </f:facet> <f:facet name="menus"> <af:menu text="Actions"> <af:commandMenuItem text="Add..." /> <af:commandMenuItem text="Create.." /> <af:commandMenuItem text="Update..." disabled="true"/> <af:commandMenuItem text="Copy"/> <af:commandMenuItem text="Delete"/> <af:commandMenuItem text="Remove" accelerator="control A"/> <af:commandMenuItem text="Preferences"/> </af:menu> </f:facet> <f:facet name="toolbar"> <af:toolbar> <af:button shortDesc="Create" icon="/new_ena.png"> </af:button> <af:button shortDesc="Update" icon="/update_ena.png"> </af:button> <af:button shortDesc="Delete" icon="/delete_ena.png"> </af:button> </af:toolbar> </f:facet> <f:facet name="secondaryToolbar"> </f:facet> <f:facet name="statusbar"> <af:toolbar> <af:outputText id="statusText" ... value="Custom Statusbar Message"/> </af:toolbar> </f:facet> <af:table rowselection="multiple" columnselection="multiple" ... <af:column ... </af:column>
Tip:
You can make menus detachable in the panelCollection
component. See Using Menus in a Menu Bar. Consider using detached menus when you expect users to do any of the following:
-
Execute similar commands repeatedly on a page.
-
Execute similar commands on different rows of data in a large table, tree table, or tree.
-
View data in long and wide tables or tree tables, and trees. Users can choose which columns or branches to hide or display with a single click.
-
Format data in long or wide tables, tree tables, or trees.
How to Add a panelCollection with a Table, Tree, or Tree Table
You add a panelCollection
component and then add the table, tree, or tree table inside the panelCollection
component. You can then add and modify the menus and toolbars for it.
Before you begin:
It may be helpful to have an understanding of how the attributes can affect functionality. See Displaying Table Menus, Toolbars, and Status Bars.
You may also find it helpful to understand functionality that can be added using other ADF Faces features. See Additional Functionality for Collection-Based Components.
To create a panelCollection component with an aggregate display component:
Displaying a Collection in a List
Using the ADF listView and listItem components you can display the contents in a single-column table structure. You can also achieve two-level hierarchy by binding listView to a TreeModel.
Instead of using a table with multiple columns, you can use the listView
and listItem
components to display structured data in a simple table-like format that contains just one column. Figure 12-32 shows a listView
component that contains one listItem
component used to display an error icon, task information, and an action button, for each row.
Figure 12-32 The listView Component with a listItem Component
Description of "Figure 12-32 The listView Component with a listItem Component"
As shown in Figure 12-33, instead of using columns to group the data to be displayed, a mix of layout components and other components, held by one listItem
component, display the actual the data. In this example, the listItem
component contains one large panelGroupLayout
component set to display its children horizontally. The children are three other panelGroupLayout
components used to group their children as columns might in a table. These panelGroupLayout
components are also set to display their children horizontally. The second of these layout components contains one panelGroupLayout
component set to display its child components (in this case three outputText
components) vertically.
Figure 12-33 The listItem Component Contains Multiple Components That Display the Data
Description of "Figure 12-33 The listItem Component Contains Multiple Components That Display the Data "
See Example 12-1 below, for the corresponding code.
You bind the listView
component to the collection. The component then repeatedly renders one listItem
component by stamping the value for each item. As each item is stamped, the data for the current row is copied into a property that can be addressed by an EL expression that uses the listView
component's var
attribute. Once the list has completed rendering, this property is removed or reverted back to its previous value.
In this example, the listView
value is bound to the demolistView.taskModel
object. The properties on this object can be accessed using the var
property, which is set to item
. For example, in order to display the task name, the outputText
component value is set to item.taskName
.
The listView
component can also display a limited, two-level hierarchy. To display a hierarchy, the listView
needs to be bound to a TreeModel
instead of a CollectionModel
. The TreeModel can contain one root level and one child level (for information about the TreeModel class, see Displaying Data in Trees).
As with trees, the listView
uses stamping to display content for the individual nodes, and a facet (named the groupHeaderStamp
facet) that acts as a holder for the component used to display the parent group for the nodes. However, since the listView
only allows two levels, the groupHeaderStamp facet contains the component used to display only the root level.
Figure 12-34 shows a listView
component displaying a simple hierarchy that has letters of the alphabet as the root, and employee objects as the leaf nodes.
Figure 12-34 Simple Hierarchy in a listView Component
Description of "Figure 12-34 Simple Hierarchy in a listView Component"
The components used to display the employee object are placed in a listItem
component, while the components used to display the letter of the alphabet are placed in a listItem
component inside the groupHeaderStamp
facet, as shown in the following example.
<af:listView id="listView" binding="#{editor.component}" var="item" varStatus="vs" groupDisclosurePolicy="noDisclosure" value="#{demolistView.ABTreeModel}"> <af:listItem id="listItem1"> <af:panelGroupLayout id="pgl3" layout="vertical"> <af:outputText id="ot2" value="#{item.ename}" styleClass="ABName"/> <af:outputText id="ot3" value="#{item.job}" styleClass="ABJob"/> </af:panelGroupLayout> </af:listItem> <f:facet name="groupHeaderStamp"> <af:listItem id="listItem2" styleClass="ABHeader"> <af:outputText id="ot1" value="#{item.alphabetHeading}"/> </af:listItem> </f:facet> </af:listView>
When you display a hierarchy in a listView
component, you can configure it so that the headers can disclose or hide its child components, as shown in Figure 12-35.
Figure 12-35 The listView Component Configured to Provide Collapsing Headers
Description of "Figure 12-35 The listView Component Configured to Provide Collapsing Headers"
By default, the listView
component is configured to display all children. You can change this using the groupDisclosurePolicy
attribute.
When a user collapses or expands a group, a RowDisclosureEvent
is fired. You can use the groupDisclosureListener
to programmatically expand and collapse nodes. See What You May Need to Know About Programmatically Expanding and Collapsing Nodes.
When a user selects or deselects a row or a node, a SelectionEvent
is fired. You can use the selectionListener
to programmatically respond to the event. See What You May Need to Know About Performing an Action on Selected Rows in Tables.
Example 12-1 The listView Component
<af:listView id="listView" binding="#{editor.component}" var="item" varStatus="vs" partialTriggers="::pprLV" value="#{demolistView.taskModel}" selection="multiple"> <af:listItem id="lvi"> <af:showPopupBehavior popupId="::ctxtMenu" triggerType="contextMenu"/> <af:panelGroupLayout id="panelGroupLayout1" layout="horizontal" styleClass="AFStretchWidth"> <af:panelGroupLayout id="panelGroupLayout2" layout="horizontal" inlineStyle="margin-left:20px; width:45px" halign="center" valign="middle"> <af:image rendered="#{vs.index %6 ==1}" source="/images/error.png" id="i1" shortDesc="Error at Line #{vs.index + 1}"/> </af:panelGroupLayout> <af:panelGroupLayout id="panelGroupLayout3" layout="horizontal" inlineStyle="width:100%"> <af:panelGroupLayout id="panelGroupLayout5" layout="vertical" inlineStyle="min-width:300px"> <af:outputText id="outputText1" value="#{item.taskName}" styleClass="taskName"/> <af:outputText id="outputText2" value="#{item.projectDesc}" styleClass="taskProjectDesc"/> <af:outputText id="outputText3" value="#{item.created}" styleClass="taskCreated"/> </af:panelGroupLayout> </af:panelGroupLayout> <af:panelGroupLayout id="panelGroupLayout4" layout="horizontal" halign="end" valign="middle" inlineStyle="margin-right:20px"> <af:button id="cb1" text="Action" shortDesc="Click To Invoke Action for Item #{vs.index + 1}"> <af:showPopupBehavior popupId="::popupDialog" alignId="cb1" align="afterStart"/> </af:button> </af:panelGroupLayout> </af:panelGroupLayout> </af:listItem> </af:listView>
How to Display a Collection in a List
You use a listView
component bound to a CollectionModel
instance and one listItem
component to create the list. If you want to display a simple parent-child hearers, you place a second listItem component in the groupHeaderStamp
facet. You then add layout components and other text components to display the actual data.
To display a collection in a list:
What You May Need to Know About Scrollbars in a List View
Similar to tables, you can configure the listView
component to use scrollbars. When you configure the listView
component to use scrolling, in iOS operating systems, by default, the scrollbars only appear when you mouseover the content. Otherwise, they remain hidden. You can configure your application so that this same behavior occurs on other operating systems as well, by adding the -tr-overflow-style: autohiding-scrollbar
skinning property to af|listView
selector.
af|listView { -tr-overflow-style: autohiding-scrollbar }
Note:
On operating systems other than iOS, initially set style attribute to overflow:hidden
for listView root element. For the mouseHover
event, set style attribute to overflow:auto
and for the mouseOut
event set style attribute back to overflow:hidden
.
Displaying Images in a Carousel
You can display images in a revolving carousel structure using ADF carouselItem
component. You can also configure the display of the carousel structure.
You can display images in a revolving carousel, as shown in Figure 12-37. Users can change the image at the front either by using the slider at the bottom or by clicking one of the auxiliary images to bring that specific image to the front.
By default, the carousel displays horizontally. The objects within the horizontal orientation of the carousel are vertically-aligned to the middle and the carousel itself is horizontally-aligned to the center of its container.
You can configure the carousel so that it can be displayed vertically, as you might want for a reference Rolodex. By default, the objects within the vertical orientation of the carousel are horizontally-aligned to the center and the carousel itself is vertically aligned middle, as shown in Figure 12-38. You can change the alignments using the carousel's alignment attributes.
Best Practice:
Generally the carousel should be placed in a parent component that stretches its children (such as a panelSplitter
or panelStretchLayout
). If you do not place the carousel in a component that stretches its children, your carousel will display at the default dimension of 500px wide and 300px tall. You can change these dimensions.
Instead of partially displaying the previous and next images, you can configure your carousel so that it displays images in a filmstrip design, as shown in Figure 12-39, or in a roomy circular design, as shown in Figure 12-40.
Figure 12-40 Carousel Roomy Circular Display
Description of "Figure 12-40 Carousel Roomy Circular Display"
By default, when the carousel is configured to display in the circular mode, when you hover over an auxiliary item (that is, and item that is not the current item at the center), the item is outlined to show that it can be selected (note that this outline will only appear if your application is using the Skyros and above skins). You can configure the carousel so that instead, the item pops out and displays at full size, as shown in Figure 12-41.
Figure 12-41 Auxiliary Item Pops Out on Hover
Description of "Figure 12-41 Auxiliary Item Pops Out on Hover"
You can also configure your carousel so that it displays only the current image, as shown in Figure 12-42.
Figure 12-42 Carousel Can Display Just One Image
Description of "Figure 12-42 Carousel Can Display Just One Image"
You can configure the controls used to browse through the images. You can display a slider with next and previous arrows that spans more than one image, display only next and previous buttons, as shown in Figure 12-42, or display next and previous buttons, along with the slide counter, as shown in Figure 12-43.
Figure 12-43 Next and Previous Buttons Without a Slider
Description of "Figure 12-43 Next and Previous Buttons Without a Slider"
A child carouselItem
component displays the objects in the carousel, along with a title for the object. Instead of creating a carouselItem
component for each object to be displayed, and then binding these components to the individual object, you bind the carousel
component to a complete collection. The component then repeatedly renders one carouselItem
component by stamping the value for each item, similar to the way a tree stamps out each row of data. As each item is stamped, the data for the current item is copied into a property that can be addressed using an EL expression using the carousel
component's var
attribute. Once the carousel has completed rendering, this property is removed or reverted back to its previous value. Carousels contain a nodeStamp
facet, which is both a holder for the carouselItem
component used to display the text and short description for each item, and also the parent component to the image displayed for each item.
For example, the carouselItem
JSF page in the ADF Faces Components Demo shown in Figure 12-37 contains a carousel
component that displays an image of each of the ADF Faces components. The demoCarouselItem
(CarouselBean.java
) managed bean contains a list of each of these components. The value attribute of the carousel
component is bound to the items
property on that bean, which represents that list. The carousel component's var
attribute is used to hold the value for each item to display, and is used by both the carouselItem
component and image
component to retrieve the correct values for each item. The following example shows the JSF page code for the carousel. For information about stamping behavior in a carousel, see Displaying Data in Trees.
<af:carousel id="carousel" binding="#{editor.component}" var="item" value="#{demoCarousel.items}" carouselSpinListener="#{demoCarousel.handleCarouselSpin}"> <f:facet name="nodeStamp"> <af:carouselItem id="crslItem" text="#{item.title}" shortDesc="#{item.title}"> <af:image id="img" source="#{item.url}" shortDesc="#{item.title}"/> </af:carouselItem> </f:facet> </af:carousel>
A carouselItem
component stretches its sole child component. If you place a single image
component inside of the carouselItem
, the image stretches to fit within the square allocated for the item (as the user spins the carousel, these dimensions shrink or grow).
Best Practice:
The image
component does not provide any geometry management controls for altering how it behaves when stretched. You should use images that have equal width and height dimensions in order for the image to retain its proper aspect ratio when it is being stretched.
The carousel
component uses a Collection
Model
class to access the data in the underlying collection. This class extends the JSF DataModel
class and adds on support for row keys. In the DataModel
class, rows are identified entirely by index. However, to avoid issues if the underlying data changes, the CollectionModel
class is based on row keys instead of indexes.
You may also use other model classes, such as java.util.List
, array
, and javax.faces.model.DataModel
. If you use one of these other classes, the carousel
component automatically converts the instance into a CollectionModel
class, but without any additional functionality. For information about the CollectionModel
class, see the MyFaces Trinidad Javadoc at http://myfaces.apache.org/trinidad/trinidad-1_2/trinidad-api/apidocs/index.html
.
Note:
If your application uses the Fusion technology stack, you can create ADF Business Components over your data source that represent the items, and the model will be created for you. You can then declaratively create the carousel, and it will automatically be bound to that model. See the "Using the ADF Faces Carousel Component" section in Developing Fusion Web Applications with Oracle Application Development Framework.
The carousel components are virtualized, meaning not all the items that are there for the component on the server are delivered to and displayed on the client. You configure the carousel to fetch a certain number of rows at a time from your data source. The data can be delivered to the component either immediately upon rendering, or lazily fetched after the shell of the component has been rendered. By default, the carousel lazily fetches data for the initial request. When a page contains one or more of these components, the page initially goes through the standard lifecycle. However, instead of the carousel fetching the data during that initial request, a special separate partial page rendering (PPR) request is run on the component, and the number of items set as the value of the fetch size for the carousel is then returned. Because the page has just been rendered, only the Render Response phase executes for the carousel, allowing the corresponding data to be fetched and displayed. When a user does something to cause a subsequent data fetch (for example spinning the carousel for another set of images), another PPR request is executed.
Performance Tip:
You should use lazy delivery when the page contains a number of components other than a carousel. Using lazy delivery allows the initial page layout and other components to be rendered first before the data is available.
Use immediate delivery if the carousel is the only context on the page, or if the carousel is not expected to return a large set of items. In this case, response time will be faster than using lazy delivery (or in some cases, simply perceived as faster), as the second request will not go to the server, providing a faster user response time and better server CPU utilizations. Note however that only the number of items configured to be the fetch block will be initially returned. As with lazy delivery, when a user's actions cause a subsequent data fetch, the next set of items are delivered.
A slider control allows users to navigate through the collection. Normally the thumb on the slider displays the current object number out of the total number of objects, for example 6 of 20. When the total number of objects is too high to calculate, the thumb on the slider will show only the current object number. For example, say a carousel is used for a company' s employee directory. By default the directory might show faces for every employee, but it may not know without an expensive database call that there are exactly 94,409 employees in the system that day.
You can use other components in conjunction with the carousel. For example, you can add a toolbar or menu bar, and to that, add buttons or menu items that allow users to perform actions on the current object.
How to Create a Carousel
To create a carousel, you must first create the data model that contains the images to display. You then bind a carousel
component to that model and insert a carouselItem
component into the nodeStamp
facet of the carousel. Lastly, you insert an image
component (or other components that contain an image
component) as a child to the carouselItem
component.
Before you begin:
It may be helpful to have an understanding of how the attributes can affect functionality. See Displaying Images in a Carousel.
You may also find it helpful to understand functionality that can be added using other ADF Faces features. See Additional Functionality for Collection-Based Components.
Create the data model that will provide the collection of images to display. The data model can be a List
, Array
, DataModel
or CollectionModel
. If the collection is anything other than a CollectionModel
, the framework will automatically convert it to a CollectionModel
. For information about the CollectionModel
class, see the MyFaces Trinidad Javadoc at http://myfaces.apache.org/trinidad/trinidad-1_2/trinidad-api/apidocs/index.html
.
The data model should provide the following information for each of the images to be displayed in the carousel:
-
URL to the images
-
Title, which will be displayed below the image in the carousel
-
Short description used for text displayed when the user mouses over the image
For examples, see the CarouselBean.java
and the CarouselMediaBean.java
classes in the ADF Faces Components Demo application.
To Create a Carousel:
What You May Need to Know About the Carousel Component and Different Browsers
In some browsers, the visual decoration of the carousel's items will be richer. For example, Safari and Google Chrome display subtle shadows around the carousel's items, and the noncurrent items have a brightness overlay to help make clear that the auxiliary items are not the current item, as shown in Figure 12-45.
Figure 12-45 Carousel Component Displayed in Google Chrome
Description of "Figure 12-45 Carousel Component Displayed in Google Chrome"
Figure 12-46 shows the same component in Internet Explorer.
Figure 12-46 Carousel Component Displayed in Microsoft Internet Explorer
Description of "Figure 12-46 Carousel Component Displayed in Microsoft Internet Explorer"
Exporting Data from Table, Tree, or Tree Tables
You can export data from ADF Gantt chart to an Excel spreadsheet or you can export as a comma-separated values file. The formatting information is also exported along with data.
You can export the data from a table, tree, or tree table, or from a table region of the data visualization project Gantt chart to a Microsoft Excel spreadsheet or to a comma-separated values (CSV) file. To allow users to export a table, you create an action source, such as a button or link that will be used to invoke the export, and add an exportCollectionActionListener
component and associate it with the data you wish to export. The formatting information is included in the exported Excel spreadsheet, so that the data types are identified correctly and you can use the inbuilt functions of the MS Excel on various columns. However, if the cell format is not valid, then by default the text is exported as Text
format. You can configure the exportCollectionActionListener
so that all the rows of the source table or tree will be exported, or so that only the rows selected by the user will be exported.
Tip:
You can also export data from a DVT pivot table. See Exporting from a Pivot Table.
For example, Figure 12-47 shows the table from the ADF Faces Components Demo application that includes buttons that allow users to export the data to an Excel spreadsheet or as a CSV file.
Figure 12-47 Table with Buttons for Exporting Data
Description of "Figure 12-47 Table with Buttons for Exporting Data"
When the user clicks a button, the listener processes the exporting of all the rows to a spreadsheet or CSV. As shown in Figure 12-47, you can also configure the exportCollectionActionListener
component so that only the rows the user selects are exported.
Only the following can be exported:
-
Value of value holder components (such as
input
andoutput
components). -
Value of
selectItem
components used inselelctOneChoice
andselectOneListbox
components (the value ofselectItem
components in other selection components are not exported). -
Value of the
text
attribute of a command component. -
Value of the
shortDesc
attribute onimage
andicon
components.
If you do not want the value of the shortDesc
attribute on image
and icon
components to be exported (for example, if a cell contains an image), you can use the predefined skipObjectComponent
filter method. This method will run before the exportCollectionActionListener
, and will keep any Object
components from being exported. You can also create your own custom filter method to apply any needed logic before the exportCollectionActionListener
runs.
Depending on the browser, and the configuration of the listener, the browser will either open a dialog, allowing the user to either open or save the file as shown in Figure 12-48, or the file will be displayed in the browser. For example, if the user is viewing the page in Microsoft Internet Explorer, and no file name has been specified on the exportCollectionActionListener
component, the file is displayed in the browser. In Mozilla Firefox, the dialog opens.
If the user chooses to save the file, it can later be opened in a spreadsheet application, as shown in Figure 12-49. If the user chooses to open the file, what happens depends on the browser. For example, if the user is viewing the page in Microsoft Internet Explorer and is exporting a spreadsheet, the spreadsheet opens in the browser window. If the user is viewing the page in Mozilla Firefox, the spreadsheet opens in Excel.
Note:
For spreadsheets, you may receive a warning from Excel stating that the file is in a different format than specified by the file extension. This warning can be safely ignored.
How to Export Table, Tree, or Tree Table Data to an External Format
You create a command component, such as a button, link, or menu item, and add the exportCollectionActionListener
inside this component. Then you associate the data collection you want to export by setting the exportCollectionActionListener
component's exportedId
attribute to the ID of the collection component whose data you wish to export.
Before you begin:
It may be helpful to have an understanding of how the attributes can affect functionality. See Exporting Data from Table, Tree, or Tree Table.
You may also find it helpful to understand functionality that can be added using other ADF Faces features. See Additional Functionality for Collection-Based Components.
You should already have a table, tree, or tree table on your page. If you do not, follow the instructions in this chapter to create a table, tree, or tree table. For example, to add a table, see Displaying Data in Tables.
Tip:
If you want users to be able to select rows to export, then configure your table to allow selection. See Formatting Tables.
To export collection data to an external format:
The following example shows the code for a table and its exportCollectionActionListener
component. Note that the exportedId
value is set to the table id
value.
<af:table contextMenuId="thePopup" selectionListener="#{fs.Table}" rowselection="multiple" columnselection="multiple" columnBandingInterval="1" binding="#{editor.component}" var="test1" value="#{tableTestData}" id="table" summary="table data"> <af:column> . . . </af:column> </af:table> <af:button text="Export To Excel" immediate="true"> <af:exportCollectionActionListener type="excelHTML" exportedId="table" filename="export.xls" title="ADF Faces Export"/>
What Happens at Runtime: How Row Selection Affects the Exported Data
Exported data is exported in index order, not selected key order. This means that if you allow selected rows to be exported, and the user selects rows (in this order) 8, 4, and 2, then the rows will be exported and displayed in Excel in the order 2, 4, 8.
Accessing Selected Values on the Client from Collection-Based Components
In the ADF framework, the client-support for EL is not available. To access row-specific data on the client, you must use EL on the collection-based component.
Since there is no client-side support for EL in the ADF Faces framework, nor is there support for sending entire collection models to the client, if you need to access values on the client using JavaScript, the client-side code cannot rely on component stamping to access the value. Instead of reusing the same component instance on each row, a new JavaScript component is created on each row (assuming any component needs to be created at all for any of the rows), using the fully resolved EL expressions.
Therefore, to access row-specific data on the client, you need to use the collection-based component itself to access the value. To do this without a client-side data model, you use a client-side selection change listener.
How to Access Values from a Selection in Stamped Components.
To access values on the client from a collection-based component, you first need to make sure the component has a client representation. Then you need to register a selection change listener on the client and then have that listener handle determining the selected row, finding the associated stamped component for that row, use the collection component to determine the row-specific name, and finally interact with the selected data as needed.
Before you begin:
You may also find it helpful to understand functionality that can be added using other ADF Faces features. See Additional Functionality for Collection-Based Components.
To access selected values from collection-based components:
The following example shows the entire code for the JavaScript.
function showSelectedName(event) { var firstRowKey; var addedRowKeys = event.getAddedSet(); for (var rowKey in addedRowKeys) { firstRowKey = rowKey; break; } // The table needs to find the stamped component. // Fortunately, in the case of selection events, the // table is the event source. var table = event.getSource(); // We use the table to find the name stamp component by id/row key: var nameStamp = table.findComponent("nameStamp", firstRowKey); if (nameStamp) { // This is the row-specific name var name = nameStamp.getValue(); alert("The selected name is: " + name); } }
What You May Need to Know About Accessing Selected Values
Row keys are tokenized on the server, which means that the row key on the client may have no resemblance to the row key on the server. As such, only row keys that are served up by the client-side APIs (like AdfSelectionEvent.getAddedSet()
) are valid.
Also note that AdfUITable.findComponent(id, rowKey)
method may return null
if the corresponding row has been scrolled off screen and is no longer available on the client. Always check for null
return values from AdfUITable.findComponent()
method.