32 Creating ADF Databound Tables
This chapter includes the following sections:
About Creating ADF Databound Tables
A table is used to display tabular data. You can bind the ADF Faces table component to the complete collection of data objects at a time from the collection. You can create and modify data in the table.
Unlike forms, tables allow you to display more than one data object from a collection at a time.
You can create tables that simply display data, or you can create tables that allow you to edit or create data. Once you drop a collection as a table, you can add command buttons bound to actions that execute some logic on a selected row. You can also modify the default components to suit your needs.
You can also display a collection of objects in a list that uses a grid to display each object's attributes, similar to a simple table.
ADF Databound Tables Use Cases and Examples
Figure 32-1 shows the Products table on the Inventory Control tab of the Summit sample application for Oracle ADF. This table is used to display all of the products in the inventory and enables the user to select a product to see its inventory status in an adjoining graph.
You can create a table that simply displays information, as shown in Figure 32-1, or you can allow the user to edit the information and create new records, as shown in Figure 32-2.
Additional Functionality for Databound Tables
You may find it helpful to understand other ADF Faces features before you implement your table and tree components. Additionally, once you have added a tree or table 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.
-
ADF view objects: Much of how the components display and function in the table is controlled by the corresponding view objects. See Defining SQL Queries Using View Objects.
-
ADF application modules: The Data Controls panel, from which you drag databound components to your pages, is populated with representations of view objects that you have added to application modules. See Implementing Business Services with Application Modules.
-
Adapter-based data controls: If you are using other types of business services, such as EJB components or web services, you can create data control for those business services as described in Creating and Configuring EJB Data Controls of the Developing Applications with Oracle ADF Data Controls.
-
ADF Model and data binding: When you create databound tables in an ADF web application, you use ADF Model and data binding. See Using ADF Model in a Fusion Web Application.
-
ADF Faces: For detailed information about developing with ADF Faces, see Introduction to ADF Faces in Developing Web User Interfaces with Oracle ADF Faces.
-
Command buttons: You may want to add command button or links that invoke some functionality against the data set. For more advanced functionality than what is covered in this chapter, see Using Command Components to Invoke Functionality in the View Layer.
-
Filtered tables: You can create tables that provide filtering, in effect executing a query-by-example search against the data in the table. See Creating Standalone Filtered Search Tables from Named View Criteria.
-
Task flows: If your table takes part in a transaction, for example an input table, then you may need to use an ADF task flow to invoke certain operations before or after the table is rendered. See Creating ADF Task Flows .
-
Validation: You may want certain fields to be validated before they are submitted to the data store. See Defining Validation and Business Rules Declaratively and Using Validation in the ADF Model Layer .
-
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 .
Note:
If you wish to use active data, and your application uses ADF Business Components, then your tables must conform to the following:
-
The binding represents homogeneous data (that is, only one rule), although an accessor can still access a like accessor.
-
The binding rule contains a single attribute.
-
The table does not use filtering.
-
The tree component's
nodeStamp
facet contains a singleoutputText
tag and contains no other tags.
-
Creating a Basic Table
In order to create a table using data control, you need to bind the ADF Faces table component to a collection of data objects. With the help of JDeveloper, you can perform this declaratively by dragging and dropping a collection from the Data Controls panel.
Unlike with forms where you bind the individual UI components that make up a form to the individual attributes on the collection, with a table you bind the ADF Faces table
component to the complete collection or to a range of n data objects at a time from the collection. The individual components used to display the data in the columns are then bound to the attributes. The iterator binding handles displaying the correct data for each object, while the table
component handles displaying each object in a row. JDeveloper allows you to do this declaratively, so that you don't need to write any code.
How to Create a Basic Table
To create a table using a data control, you bind the table
component to a collection. JDeveloper allows you to do this declaratively by dragging and dropping a collection from the Data Controls panel.
Before you begin:
It may be helpful to have an understanding of ADF Faces databound tables. For more information, see Creating a Basic Table.
You may also find it helpful to understand other ADF functionality and features. For more information, see Additional Functionality for Databound Tables.
You will need to complete these tasks:
-
Create an application module that contains instances of the view objects that you want in your data model, as described in Creating and Modifying an Application Module. Alternatively, if you are using another type of business service, create a data control for that business service as described in Exposing Business Services with Data Controls in Developing Applications with Oracle ADF Data Controls.
-
Create a JSF page as described in Creating a Web Page.
To create a databound table:
What Happens When You Create a Table
Dropping a table from the Data Controls panel has the same effect as dropping a text field or form. Briefly, JDeveloper does the following:
-
Creates the bindings for the table and adds the bindings to the page definition file
-
Adds the necessary code for the UI components to the JSF page
For more information, see What Happens When You Create a Text Field .
Iterator and Value Bindings for Tables
When you drop a table from the Data Controls panel, a tree
value binding is created. A tree consists of a hierarchy of nodes, where each subnode is a branch off a higher level node. In the case of a table, it is a flattened hierarchy, where each attribute (column) is a subnode off the table. Like an attribute binding used in forms, the tree
value binding references the iterator binding, while the iterator binding references an iterator for the data collection, which facilitates iterating over the data objects in the collection. Instead of creating a separate binding for each attribute, only the tree binding to the table node is created. In the tree binding, the AttrNames
element within the nodeDefinition
element contains a child element for each attribute that you want to be available for display or reference in each row of the table.
The tree value binding is an instance of the FacesCtrlHierBinding
class that extends the core JUCtrlHierBinding
class to add two JSF specific properties:
-
collectionModel
: Returns the data wrapped by an object that extends thejavax.faces.model.DataModel
object that JSF and ADF Faces use for collection-valued components like tables. -
treeModel
: ExtendscollectionModel
to return data that is hierarchical in nature. For more information, see Displaying Master-Detail Data.
The following example shows the value binding for the table created when you drop the Summit sample application for ADF task flows's ProductVO1
collection.
<bindings> <tree IterBinding="ProductVO1Iterator" id="ProductVO1"> <nodeDefinition DefName="oracle.summit.model.views.ProductVO" Name="ProductVO10"> <AttrNames> <Item Value="Id"/> <Item Value="Name"/> <Item Value="ShortDesc"/> <Item Value="LongtextId"/> <Item Value="ImageId"/> <Item Value="SuggestedWhlslPrice"/> <Item Value="WhlslUnits"/> </AttrNames> </nodeDefinition> </tree> </bindings>
Only the table component needs to be bound to the model (as opposed to the columns or the text components within the individual cells), because only the table needs access to the data. The tree binding for the table drills down to the individual structure attributes in the table, and the table columns can then derive their information from the table component.
Code on the JSF Page for an ADF Faces Table
When you use the Data Controls panel to drop a table onto a JSF page, JDeveloper inserts an ADF Faces table
component, which contains an ADF Faces column
component for each attribute named in the table binding. Each column then contains another component (such as an inputText
or outputText
component) bound to the attribute's value. Each column's heading is bound to the labels
property for the control hint of the attribute.
Tip:
If an attribute is marked as hidden on the associated view or entity object, no corresponding UI is created for it.
The following example shows a code excerpt from a table created by dropping the ProductsVO1
collection as a read- only table.
<af:table value="#{bindings.ProductVO1.collectionModel}" var="row" rows="#{bindings.ProductVO1.rangeSize}" emptyText="#{bindings.ProductVO1.viewable ? 'No data to display.' : 'Access Denied.'}" fetchSize="#{bindings.ProductVO1.rangeSize}" rowBandingInterval="0" selectedRowKeys="#{bindings.ProductVO1.collectionModel.selectedRow}" selectionListener="#{bindings.ProductVO1.collectionModel.makeCurrent}" rowSelection="single" id="t1"> <af:column headerText="#{bindings.ProductVO1.hints.Id.label}" id="c1"> <af:outputText value="#{row.Id}" shortDesc="#{bindings.ProductVO1.hints.Id.tooltip}" id="ot1"> <af:convertNumber groupingUsed="false" pattern="#{bindings.ProductVO1.hints.Id.format}"/> </af:outputText> </af:column> <af:column headerText="#{bindings.ProductVO1.hints.Name.label}" id="c2"> <af:outputText value="#{row.Name}" shortDesc="#{bindings.ProductVO1.hints.Name.tooltip}" id="ot2"/> </af:column> <af:column headerText="#{bindings.ProductVO1.hints.ShortDesc.label}" id="c3"> <af:outputText value="#{row.ShortDesc}" shortDesc="#{bindings.ProductVO1.hints.ShortDesc.tooltip}" id="ot3"/> </af:column> . . . </af:table>
The tree binding iterates over the data exposed by the iterator binding. Note that the table's value is bound to the collectionModel
property, which accesses the collectionModel
object. The table wraps the result set from the iterator binding in a collectionModel
object. The collectionModel
allows each item in the collection to be available within the table component using the var
attribute.
In the example, the table iterates over the rows in the current range of the ProductVO1
iterator binding. The iterator binding binds to a row set iterator that keeps track of the current row. When you set the var
attribute on the table to row
, each column then accesses the current data object for the current row presented to the table tag using the row
variable, as shown for the value of the af:outputText
tag:
<af:outputText value="#{row.Id}"/>
When you drop an ADF Table and do not specify it as a read-only table, input components are generated instead of output components, as shown in the following example. The value of the input component is implicitly bound to a specific row in the binding container through the bindings
property, instead of being bound to the row variable. Additionally, JDeveloper adds validator components for each input component. By using the bindings property, any raised exception can be linked to the corresponding binding object or objects. The controller iterates through all exceptions in the binding container and retrieves the binding object to get the client ID when creating FacesMessage
objects. This retrieval allows the table to display errors for specific cells. This strategy is used for all input components, including selection components such as lists.
<af:table value="#{bindings.ProductVO1.collectionModel}" var="row" rows="#{bindings.ProductVO1.rangeSize}" emptyText="#{bindings.ProductVO1.viewable ? 'No data to display.' : 'Access Denied.'}" fetchSize="#{bindings.ProductVO1.rangeSize}" rowBandingInterval="0" id="t1"> <af:column headerText="#{bindings.ProductVO1.hints.Id.label}" id="c1"> <af:inputText value="#{row.bindings.Id.inputValue}" label="#{bindings.ProductVO1.hints.Id.label}" required="#{bindings.ProductVO1.hints.Id.mandatory}" columns="#{bindings.ProductVO1.hints.Id.displayWidth}" maximumLength="#{bindings.ProductVO1.hints.Id.precision}" shortDesc="#{bindings.ProductVO1.hints.Id.tooltip}" id="it1"> <f:validator binding="#{row.bindings.Id.validator}"/> <af:convertNumber groupingUsed="false" pattern="#{bindings.ProductVO1.hints.Id.format}"/> </af:inputText> </af:column>
For more information about using ADF Faces validators and converters, see the Validating and Converting Input chapter of Developing Web User Interfaces with Oracle ADF Faces.
Table 32-1 shows the other attributes defined by default for ADF Faces tables created using the Data Controls panel.
Table 32-1 ADF Faces Table Attributes and Populated Values
Attribute | Description | Default Value |
---|---|---|
|
Determines how many rows to display at one time. |
An EL expression that, by default, evaluates to the |
|
Index of the first row in a range (based on 0). |
An EL expression that evaluates to the |
|
Text to display when there are no rows to return. |
An EL expression that evaluates to the viewable property on the iterator. If the table is viewable, the attribute displays No data to display when no objects are returned. If the table is not viewable (for example, if there are authorization restrictions set against the table), it displays Access Denied. |
|
Number of rows of data fetched from the data source. |
An EL expression that, by default, evaluates to the Note that to improve scrolling behavior in a table, when the table's iterator binding is expected to manage a data set consisting of over 200 items, and the view object is configured to use range paging, the iterator actually returns a set of ranges instead of just one range. For more information about using range paging for view objects, see Using Range Paging to Efficiently Scroll Through Large Result Sets. For more information on range paging for JPA-based data controls, see Paginated Fetching of Data in EJB Data Controls in Developing Applications with Oracle ADF Data Controls. |
|
The selection state for the table. |
An EL expression that by default, evaluates to the selected row on the collection model. |
|
Reference to a method that listens for a selection event. |
An EL expression that by default, evaluates to the |
|
Determines whether rows are selectable. |
Set to |
Column Attributes |
||
|
Determines the property on which to sort the column. |
Set to the columns corresponding attribute binding value. |
|
Determines whether a column can be sorted. |
Set to |
|
Determines the text displayed at the top of the column. |
An EL expression that, by default, evaluates to the label control hint set on the corresponding attribute. |
What You May Need to Know About Setting the Current Row in a Table
When you use tables in an application and you allow the ADF Model layer to manage row selection, the current row is determined by the iterator. When a user selects a row in an ADF Faces table, the row in the table is shaded, and the component notifies the iterator of the selected row. To do this, the selectedRowKeys
attribute of the table is bound to the collection model's selected row, as shown in the following example.
<af:table value="#{bindings.ProductVO1.collectionModel}" var="row" . . . selectedRowKeys="#{bindings.ProductVO1.collectionModel.selectedRow}" selectionListener="#{bindings.ProductVO1.collectionModel.makeCurrent}" rowSelection="single">
This binding binds the selected keys in the table to the selected row of the collection model. The selectionListener
attribute is then bound to the collection model's makeCurrent
property. This binding makes the selected row of the collection the current row of the iterator.
Note:
If you create a custom selection listener, you must create a method binding to the makeCurrent
property on the collection model (for example #{binding.Products.collectionModel.makeCurrent})
and invoke this method binding in the custom selection listener before any custom logic.
Although a table can handle selection automatically, there may be cases where you need to programmatically set the current row for an object on an iterator.
You can call the getKey()
method on any view row to get a Key
object that encapsulates the one or more key attributes that identify the row. You can also use a Key
object to find a view row in a row set using the findByKey()
method. At runtime, when either the setCurrentRowWithKey
or the setCurrentRowWithKeyValue
built-in operation is invoked by name by the data binding layer, the findByKey()
method is used to find the row based on the value passed in as a parameter before the found row is set as the current row.
The setCurrentRowWithKey
and setCurrentRowWithKeyValue
operations both expect a parameter named rowKey
, but they differ precisely by what each expects that rowKey
parameter value to be at runtime:
- setCurrentRowWithKey Operation
-
setCurrentRowWithKey
expects therowKey
parameter value to be the serialized string representation of a view row key. This is a hexadecimal-encoded string that looks similar to this:000200000002C20200000002C102000000010000010A5AB7DAD9
The serialized string representation of a key encodes all of the key attributes that might comprise a view row's key in a way that can be conveniently passed as a single value in a browser URL string or form parameter. At runtime, if you inadvertently pass a parameter value that is not a legal serialized string key, you may receive exceptions like
oracle.jbo.InvalidParamException
orjava.io.EOFException
as a result. In your web page, you can access the value of the serialized string key of a row by referencing therowKeyStr
property of an ADF control binding (for example.#{bindings.SomeAttrName.rowKeyStr}
) or the row variable of an ADF Faces table (e.g.#{row.rowKeyStr}
). - setCurrentRowWithKeyValue Operation
-
The
setCurrentRowWithKeyValue
operation expects therowKey
parameter value to be the literal value representing the key of the view row. For example, its value would be simply "201
" to find product number201
.
Note:
If you write custom code in an application module class and need to find a row based on a serialized string key passed from the client, you can use the getRowFromKey()
method in the JboUtil
class in the oracle.jbo.client
package:
static public Row getRowFromKey(RowSetIterator rsi, String sKey)
The first parameter is the view object instance in which you'd like to find the row. The second parameter is the serialized string format of the key.
What You May Need to Know About Getting Row Data Programmatically
CollectionModel
supports a version of the getRowData
method that takes a row key parameter and thus allows you to get row data without changing currency.
For example, you can replace this code:
CollectionModelInstance.setRowKey(RowKeyInstance); JUCtrlHierNodeBinding rowData = (JUCtrlHierNodeBinding) CollectionModelInstance.getRowData();
with this code:
JUCtrlHierNodeBinding rowData = (JUCtrlHierNodeBinding) CollectionModelInstance.getRowData(RowKeyInstance);
This is particularly useful when your view object uses range paging, since it enables you to avoid having to use the setRowKey
method. (If you use setRowKey
, you might also need to provide custom code to ensure that the row that you specify with that method is within the current range of rows.)
What You May Need to Know About Table Scrolling Behavior and Row Count
When you add the table component to a page, you size the table to display fewer rows than the fetch size set on the backing view object and therefore only a portion of the total row count. At runtime, when the user scrolls through the result set and the total number of rows is much larger than table rows, it is usually desirable to wait until the user stops scrolling and the final position of the scroller is established to render the corresponding rows. This default behavior is called delayed scrolling because the table rows don’t change until the position of the scroller is established. In contrast, when few fetches are needed to scroll through the result set (because the row count is a small multiple of the backing view object’s fetch size), smooth scrolling may be desired to allow the user to view table rows corresponding to the changing position of the scroller.
You can configure the desired table scrolling behavior using the RowCountThreshold attribute on the table’s iterator binding. The default attribute value (0) gets the estimated row count when the table is first rendered and supports delayed scrolling through a large result set. In this case, the size of the scroller will be determined from the start and will not change as the user scrolls through the result set.
To support smooth scrolling, set the RowCountThreshold attribute on the iterator binding of the table component to a value less than 0 (for example, -1). This will enforce smooth scrolling through a small result set and defer getting an estimated row count until the last set of rows is fetched. In this case, the scroller size and position will be determined by the number of rows fetched by the table, and the scroller will get smaller as the user scrolls through the result set.
Alternatively, you can set the RowCountThreshold attribute to a value greater than 0 when you want to configure a threshold for smooth scrolling to begin. When the table renders, it executes the estimated row count and if the row count is less than the value you specify, the framework will enforce the default behavior (delay row scrolling until the scroller position is established). If the estimated row count exceeds the threshold value, the table enforces the smooth scrolling behavior.
Creating an Editable Table
The process of creating an editable table is similar to that of a basic table apart from the addition of command buttons bound to operations. You can add an ADF Faces component if you need your table to contain a toolbar and place the command buttons on it.
You can create a table that allows the user to edit information within the table, and then commit those changes to the data source. You can use built-in data control operations to create command buttons for changing the data, and place those buttons in a toolbar in the table. For example, you might use the Delete
operation to create a button that allows a user to delete a record from the current range.
It is important to note that the collection-specific operations are executed only against objects in the ADF cache. You need to use the Commit
operation on the root data control to actually commit any changes to the data source. You use the data control's Rollback
operation to roll back any changes made to the cached object. If the page is part of a transaction within a bounded task flow, you would most likely use these operations to resolve the transaction in a task flow return activity. See Managing Transactions in Task Flows.
Note:
For data controls which do not have the Commit
and Rollback
operations available, such as data controls based on web services or stateless EJB session beans, you need to provide custom methods to save the data to the data source. For JPA-based data controls, you can use the mergeEntity
or persistEntity
methods to save changes, but these methods only work on a single row. For more information on working with JPA-based data controls, see About Commit Models for EJB Session Beans in Developing Applications with Oracle ADF Data Controls.
When you decide to use editable components to display your data, you have the option of the table displaying all rows as editable immediately, or displaying all rows as read-only until the user double-clicks within the row. Figure 32-4 shows a table whose rows all have editable fields. The page renders using the components that were added to the page (for example, inputText
and inputDate
, components).
Figure 32-5 shows the same table, but configured so that the user must double-click (or single-click if the row is already selected) a row in order to edit or enter data. Note that outputText
components are used to display the data in the nonselected rows, even though the same input components as in Figure 32-4 were used to build the page. The only row that actually renders those components is the row selected for editing.
For more information about how ADF Faces table components handle editing, see Editing Data in Tables, Trees, and Tree Tables in Developing Web User Interfaces with Oracle ADF Faces.
How to Create an Editable Table
To create an editable table, you follow similar procedures to creating a basic table, then you add command buttons bound to operations. However, in order for the table to contain a toolbar, you need to add an ADF Faces component that associates the toolbar with the items in the collection used to build the table.
Before you begin:
It may be helpful to have an understanding of editable databound tables. For more information, see Creating an Editable Table.
You may also find it helpful to understand other ADF functionality and features. For more information, see Additional Functionality for Databound Tables.
You will need to complete these tasks:
-
Create an application module that contains instances of the view objects that you want in your data model, as described in Creating and Modifying an Application Module. Alternatively, if you are using another type of business service, create a data control for that business service as described in Exposing Business Services with Data Controls in Developing Applications with Oracle ADF Data Controls.
-
Create a JSF page as described in Creating a Web Page.
To create an editable table:
Tip:
To create a table that allows you to insert a new record into the data store, see Creating an Input Table.
What Happens When You Create an Editable Table
Creating an editable table is similar to creating a form used to edit records. Action bindings are created for the operations dropped from the Data Controls panel. For details, see What Happens When You Create Command Components Using Operations.
Creating an Input Table
In order to create an input table, you need to first create an editable table and add a command component for creating new blank rows used to create new records.
You can create a table that allows users to insert a new blank row into a table and then add values for each column (any default values set on the corresponding entity or view object will be automatically populated).
How to Create an Input Table
When you create an input table, you want the user to see the new blank row in the context of the other rows within the current row set. To allow this insertion, you use the CreateInsert
operation. As opposed to the Create
operation, the CreateInsert
operation actually creates the new row within the row set instead of only in the cache. In addition, you need to configure the table to respond to that user action. ADF Faces components can be set so that one component refreshes based on an interaction with another component, without the whole page needing to be refreshed. This is known as partial page rendering.
Note:
The CreateInsert
operation is not available for adapter-based data controls, such as those based web services or Java classes. However, for JPA-based data controls, the Create
operation adds the new row to the row set like CreateInsert
does for data controls based on application modules. For more information, see Data Control Built-in Operations in Developing Applications with Oracle ADF Data Controls.
Before you begin:
It may be helpful to have an understanding of ADF Faces input tables. For more information, see Creating an Input Table.
You may also find it helpful to understand other ADF functionality and features. For more information, see Additional Functionality for Databound Tables.
You will need to complete these tasks:
-
Create an editable table, as described in Creating an Editable Table.
-
If your table is not part of a bounded task flow, include buttons bound to the
Commit
andRollback
operations.
To create an input table:
What Happens When You Create an Input Table
When you use the CreateInsert
operation to create an input table, JDeveloper:
-
Creates an iterator binding for the collection, an action binding for the
CreateInsert
operation, and attribute bindings for the table. TheCreateInsert
operation is responsible for creating the new row in the row set. If you created command buttons or links using theCommit
andRollback
operations, JDeveloper also creates an action bindings for those operations. -
Inserts code in the JSF page for the table using ADF Faces
table
,column
, andinputText
components, and in the case of the operations,button
components.
The following example shows the page definition file for an input table created from the ProductVO1
collection (some attributes were deleted in the Create Table dialog when the collection was dropped).
<executables> <iterator Binds="ProductVO1" RangeSize="25" DataControl="BackOfficeAppModuleDataControl" id="ProductVO1Iterator"/> </executables> <tree IterBinding="ProductVO1Iterator" id="ProductVO1"> <nodeDefinition DefName="oracle.summit.model.views.ProductVO" Name="ProductVO10"> <AttrNames> <Item Value="Id"/> <Item Value="Name"/> <Item Value="ShortDesc"/> <Item Value="LongtextId"/> <Item Value="ImageId"/> <Item Value="SuggestedWhlslPrice"/> <Item Value="WhlslUnits"/> </AttrNames> </nodeDefinition> </tree> <action IterBinding="ProductVOIterator" id="CreateInsert" RequiresUpdateModel="true" Action="createInsertRow"/> <action id="Commit" RequiresUpdateModel="true" Action="commitTransaction" DataControl="BackOfficeAppModuleDataControl"/> <action id="Rollback" RequiresUpdateModel="false" Action="rollbackTransaction" DataControl="BackOfficeAppModuleDataControl"/> </bindings>
The following example shows the code added to the JSF page that provides partial page rendering, using the CreateInsert
command toolbar button as the trigger to refresh the table.
<af:form> <af:panelCollection id="pc1"> <f:facet name="menus"/> <f:facet name="toolbar"> <af:toolbar id="tb1"> <af:button actionListener="#{bindings.CreateInsert.execute}" text="CreateInsert" disabled="#{!bindings.CreateInsert.enabled}" id="CreateInsert"/> <af:button actionListener="#{bindings.Commit.execute}" text="Commit" disabled="false" id="b2"/> <af:button actionListener="#{bindings.Rollback.execute}" text="Rollback" disabled="#{!bindings.Rollback.enabled}" immediate="true" id="b3"> <af:resetActionListener/> </af:button> </af:toolbar> </f:facet> <f:facet name="statusbar"/> <af:table value="#{bindings.ProductVO1.collectionModel}" var="row" rows="#{bindings.ProductVO1.rangeSize}" emptyText="#{bindings.ProductVO1.viewable ? \'No data to display.\' : \'Access Denied.\'}" fetchSize="#{bindings.ProductVO1.rangeSize}" rowSelection="single" partialTriggers="CreateInsert" id="t1"> <af:column sortProperty="Id" sortable="false" headerText="#{bindings.ProductVO1.hints.Id.label}" id="c1"> <af:inputText value="#{row.ProductId}" simple="true" required="#{bindings.ProductVO1.hints.Id.mandatory}" columns="#{bindings.ProductVO1.hints.Id.displayWidth}" maximuumLength="#{bindings.ProductVO1.hints.Id.precision}" id="it1"/> </af:column> . . . </af:table> </af:panelCollection> </af:form>
What Happens at Runtime: How CreateInsert and Partial Page Refresh Work
When the button bound to the CreateInsert
operation is invoked, the action executes, and a new instance for the collection is created and inserted as the page is rerendered. Because the button was configured to be a trigger that causes the table to refresh, the table redraws with the new empty row shown at the top. When the user clicks the button bound to the Commit
action, the newly created rows in the row set are inserted into the database. For more information about partial page refresh, see the Rerendering Partial Page Content chapter in Developing Web User Interfaces with Oracle ADF Faces.
What You May Need to Know About Creating a Row and Sorting Columns
If your table columns allow sorting, and the user has sorted on a column before inserting a new row, then that new row will not be sorted. To have the column sort with the new row, the user must first sort the column opposite to the desired sort, and then resort. This is because the table assumes the column is already sorted, so clicking on the desired sort order first will have no effect on the column.
For example, say a user had sorted a column in ascending order, and then added a new row. Initially, that row appears at the top. If the user first clicks to sort the column again in ascending order, the table will not resort, as it assumes the column is already in ascending order. The user must first sort on descending order and then ascending order.
If you want the data to automatically sort on a specific column in a specific order after inserting a row, then programmatically queue a SortEvent
after the commit, and implement a handler to execute the sort.
If you want to allow sorting on a column for which the view object attribute is defined by a custom domain data type, you must implement the compareTo()
method for the table's sort button click event:
public int compareTo(Object theObject)
The method will return 0
for values that are equal, less then 0
if the current object is less than theObject
, and greater than 0
if the current object is greater. The object comparison for attributes behaves just like String comparisons.
What You May Need to Know About Create and CreateInsert
When you use the Create
or CreateInsert
operation to declaratively create a new row, it performs the following lines of code:
// create a new row for the view object
Row newRow = yourViewObject
.createRow();
// mark the row as being "initialized", but not yet new
newRow.setNewRowState(Row.STATUS_INITIALIZED);
However, if you are using the CreateInsert
operation, it performs the additional line of code to insert the row into the row set:
// insert the new row into the view object's default rowset
yourViewObject
.insertRow(newRow);
When you create a row in an entity-based view object, the Transaction
object associated with the current application module immediately takes note of the fact. The new entity row that gets created behind the view row is already part of the Transaction
's list of pending changes. When a newly created row is marked as having the initialized state, it is removed from the Transaction
's pending changes list and is considered a blank row in which the end user has not yet entered any data values. The term initialized is appropriate since the end user will see the new row initialized with any default values that the underlying entity object has defined. If the user never enters any data into any attribute of that initialized row, then it is as if the row never existed. At transaction commit time, since that row is not part of the Transaction
's pending changes list, no INSERT
statement will be attempted for it.
As soon as at least one attribute in an initialized row is set, it automatically transitions from the initialized status to the new status (Row.STATUS_NEW
). At that time, the underlying entity row is enrolled in the Transaction
's list of pending changes, and the new row will be permanently saved the next time you commit the transaction.
Note:
If the end user performs steps that result in creating many initialized rows but never populating them, it might seem likely to cause a slow memory leak. However, the memory used by an initialized row that never transitions to the new state will eventually be reclaimed by the Java virtual machine's garbage collector.
What You May Need to Know About Saving Multiple Rows in a JPA-Based Data Control
EJB data controls and JPA-based bean data controls do not have the built-in CreateInsert
operation available. However, it is possible to replicate the behavior of CreateInsert
with the Create
operation in these data controls by setting the data control's EagerPersist
property to true
. This property is set to true
by default for data controls based on stateful session beans that use container-managed transactions and an explicit commit model. For more information, see About Commit Models for EJB Session Beans in Developing Applications with Oracle ADF Data Controls
Creating a List View of a Collection
The listview component uses a model to display data in a list. You can choose between a panelGroupLayout or a panelGridLayout component to arrange the components for your list item.
You can use the listView
component when you need to display a collection of objects in a list. The attributes of each object are displayed in a group or grid within a single row and single column. Figure 32-8 shows a collection of products displayed using a listView
component.
Figure 32-8 A Collection Displayed in a List in a Single Column

Description of "Figure 32-8 A Collection Displayed in a List in a Single Column"
In this listView
, a child listItem
component contains a panelGridLayout
component that, in turn, contains outputFormatted
components that display the data and a button component that allows users to delete the item. The listView
component is bound to the collection, in the same way that a table is bound to a collection, using the value
attribute. The listItem
component is a direct child to the listView
component, and is the container for the components that display the data, in the same way that the column component holds the data components for a table.
If your collection has a master-detail relationship with another collection, you can group the child collection under the parent collection using grouping functionality in the listView
component. For more information, see Using List Views to Display Master-Detail Objects.
For complete information about the listView
and listItem
components, see the Displaying a Collection in a List section of the Developing Web User Interfaces with Oracle ADF Faces. For complete information about using the panelGridLayout
component to group items in grid within the list (as shown in Figure 32-8), see the Arranging Content in a Grid section of the Developing Web User Interfaces with Oracle ADF Faces. You can also use a panelGroupLayout
component to group components when you don't need a grid layout. For more information, see the Grouping Related Items section of the Developing Web User Interfaces with Oracle ADF Faces.
How to Create a Databound List View
You use the Create List View wizard to create your list. This wizard allows you to choose between a panelGroupLayout
or a panelGridLayout
component to arrange the components for your list item. If you are creating the list for a child of a master-detail relationship, you can also use the wizard to configure the headers for the list.
Before you begin:
It may be helpful to have an understanding of ADF Faces list views. For more information, see Creating a List View of a Collection.
You may also find it helpful to understand other ADF functionality and features. For more information, see Additional Functionality for Databound Tables.
You will need to complete these tasks:
-
Create an application module that contains instances of the view objects that you want in your data model, as described in Creating and Modifying an Application Module. Alternatively, if you are using another type of business service, create a data control for that business service as described in Exposing Business Services with Data Controls in Developing Applications with Oracle ADF Data Controls.
-
Create a JSF page as described in Creating a Web Page.
To create a list view:
What Happens When You Create a Databound List View
When you use the wizard to create a listView
component, JDeveloper creates and configures the listView
, listItem
, and layout components. The bindings are the same as for a table—the listView
component is bound to the collection dropped from the Data Controls panel. The value of the var
attribute on this component is used by the outputFormatted
components to access the data, in the same way as a table. For more information about the page definition code and the JSP code for collections, see What Happens When You Create a Table.
The following example shows the page code for the simple table shown in Figure 32-8.
<af:listView value="#{bindings.SProductView1.collectionModel}" var="item" emptyText="#{bindings.SProductView1.viewable ? 'No data to display.' : 'Access Denied.'}" fetchSize="#{bindings.SProductView1.rangeSize}" id="lv1"> <af:listItem id="li1"> <af:panelGridLayout id="pgl2" dimensionsFrom="parent"> <af:gridRow marginTop="5px" height="15px" id="gr2"> <af:gridCell marginStart="5px" width="50%" id="gc2"> <af:panelLabelAndMessage label="Product:" id="plam1" inlineStyle="font-weight:bold;"> <af:outputFormatted value="#{item.bindings.Name.inputValue}" id="of1"/> </af:panelLabelAndMessage> </af:gridCell> <af:gridCell marginStart="5px" width="50%" marginEnd="5px" id="gc3"/> </af:gridRow> <af:gridRow marginTop="5px" height="15px" id="gr3"> <af:gridCell marginStart="5px" width="50%" id="gc4"> <af:panelLabelAndMessage label="ID:" id="plam2"> <af:outputFormatted value="#{item.bindings.Id.inputValue}" id="of2"> <af:convertNumber groupingUsed="false" pattern="#{bindings.SProductView1.hints.Id.format}"/> </af:outputFormatted> </af:panelLabelAndMessage> </af:gridCell> <af:gridCell marginStart="5px" width="50%" marginEnd="5px" id="gc5"> <af:button actionListener="#{bindings.Delete.execute}" text="Delete Product" disabled="#{!bindings.Delete.enabled}" id="b1"/> </af:gridCell> </af:gridRow> <af:gridRow marginTop="5px" height="15px" marginBottom="5px" id="gr4"> <af:gridCell marginStart="5px" width="50%" id="gc6"> <af:panelLabelAndMessage label="Suggested Wholesale:" id="plam3"> <af:outputFormatted value="#{item.bindings.SuggestedWhlslPrice.inputValue}" id="of3"> <af:convertNumber groupingUsed="false" pattern="#{bindings.SProductView1.hints.SuggestedWhlslPrice.format}"/> </af:outputFormatted> </af:panelLabelAndMessage> </af:gridCell> <af:gridCell marginStart="5px" width="50%" marginEnd="5px" id="gc7"/> </af:gridRow> </af:panelGridLayout> </af:listItem> </af:listView>
Creating a Table with Dynamic Components
A table can be created using ADF Faces dynamic components. The dynamic component in the table helps you to determine the binding metadata and the tags used to display the bound content each time you run it.
Instead of creating static databound tables where you provide tags for each component directly in the page, you can use a dynamic component to create tables where the binding metadata, columns, and the components used to display the bound content are determined at runtime.
Figure 32-9 shows a dynamic table at runtime that was created by setting UI hints for attributes on the CustomerVO
view object and then dropping the Customers
data control collection as a dynamic table. Among the UI hints set were LABEL
and DISPLAYHINT
(the latter of which can be set to Hide
in order to not include the given attribute in the dynamic form).
Figure 32-9 Dynamic Table Including a Column Group Based on Category Hint

Description of "Figure 32-9 Dynamic Table Including a Column Group Based on Category Hint"
For more general information on dynamic components, see About Dynamic Components.
How to Create a Dynamic Table
To create a dynamic table, you drop a collection from the data controls panel as an ADF Table and specify that the fields be generated dynamically.
Before you begin:
It may be helpful to have an understanding of the dynamic component feature and dynamic databound tables. For more information, see About Dynamic Components and Creating a Table with Dynamic Components.
You may also find it helpful to understand other ADF functionality and features. For more information, see Additional Functionality for Databound Tables.
You will need to complete these tasks:
-
Create an application module that contains instances of the view objects that you want in your data model, as described in Creating and Modifying an Application Module. Alternatively, if you are using another type of business service, create a data control for that business service as described in Exposing Business Services with Data Controls in Developing Applications with Oracle ADF Data Controls.
-
Create a JSF page as described in Creating a Web Page.
UI hints determine things such as the type of UI component to use to display the attribute, the label, the tooltip, whether the field should be automatically submitted, and so forth. You can also determine whether a given attribute is displayed or hidden. For the procedure to create UI hints, see Defining UI Hints for View Objects.
To create a dynamic table:
What Happens When You Use Dynamic Components
When you drop a collection as a dynamic table on a page, the following things happen:
-
The page definition is populated with a
variableIterator
binding, aniterator
binding to the iterator, and atree
value binding. See Bindings Created for the Dynamic Form. -
The JSF page is populated with an
af:table
tag and one or moreaf:iterator
,af:column
andaf:dynamicComponent
tags. In addition, if the Include Column Groups option is selected,af:switcher
andaf:group
tags are added.
Tags Created for a Dynamic Table without Grouping
In the JSF page, JDeveloper inserts an af:table
tag, within which it nests an af:iterator
tag that iterates over the attributes of the collection. Within that af:iterator
tag is nested an af:column
tag, which contains an af:dynamicComponent
tag. The af:dynamicComponent
in turn displays the appropriate component for each column as determined at runtime.
The following example shows the code that is generated when you drop the Countries
data control object as a dynamic table (but do not select the Include Column Groups option).
<af:table value="#{bindings.Countries.collectionModel}" var="row" rows="#{bindings.Countries.rangeSize}" emptyText="#{bindings.Countries.viewable ? 'No data to display.' : 'Access Denied.'}" rowBandingInterval="0" fetchSize="#{bindings.Countries.rangeSize}" id="t1"> <af:iterator id="i1" value="#{bindings.Countries.attributesModel.attributes}" var="column"> <af:column headerText="#{column.label}" id="c1"> <af:dynamicComponent id="d2" attributeModel="#{column}" value="#{row.bindings[column.name].inputValue}"/> </af:column> </af:iterator> </af:table>
The value
attribute of the af:iterator
tag uses an EL expression that evaluates to the attributesModel.attributes
property of the collection's tree binding. The attributesModel
property is used to retrieve the data object's attributes and their metadata, such as component type, label, tooltip, and other properties of the real component to be rendered. The attributes
property of attributesModel
signifies that a flat (unhierarchical) list of displayable attributes and their metadata is provided.
The attributeModel
attribute of the af:dynamicComponent
tag is bound to the EL expression #{column}
, which references the variable that is defined in the iterator's var
attribute and that serves as a pointer to the current attribute of the data control collection and its corresponding metadata. The EL expression for the dynamicComponent
's value
attribute also references the variable column
.
Tags Created for a Dynamic Table with Grouping
If you have selected the Include Column Groups checkbox in the Create Table dialog, the generated JSF page includes the af:switcher
and af:group
tags, in addition to the tags described in Tags Created for a Dynamic Table without Grouping.
The af:switcher
tag is nested directly within an af:column
tag. Within the af:switcher
tag are nested facet
tags named GROUP
and ATTRIBUTE
. The GROUP
facet contains an af:group
tag, within which is an af:outputText
tag to display the group name and an af:iterator
tag, which contains an af:column
tag that in turn contains an af:dynamicComponent
tag. The ATTRIBUTE
facet only contains an af:dynamicComponent
tag.
For each attribute that the high-level iterator
iterates over, the switcher dynamically determines whether to render a group or a column. If it renders a group, the iterator within the group then is used to render the columns within that group.
The following example shows the code that is generated if you create a dynamic form based on the Countries
collection and choose to include column groups.
<af:table value="#{bindings.Countries.collectionModel}" var="row" rows="#{bindings.Countries.rangeSize}" emptyText="#{bindings.Countries.viewable ? 'No data to display.' : 'Access Denied.'}" rowBandingInterval="0" fetchSize="#{bindings.Countries.rangeSize}" id="t1"> <af:iterator id="i1" value="#{bindings.Countries.attributesModel.hierarchicalAttributes}" var="column"> <af:column headerText="#{column.label}" id="c1"> <af:switcher id="sw1" facetName="#{column.descriptorType}" defaultFacet="ATTRIBUTE"> <f:facet name="GROUP"> <af:iterator id="gi1" value="#{column.descriptors}" var="nestedCol"> <af:column headerText="#{nestedCol.label}" id="c2"> <af:dynamicComponent id="gd1" attributeModel="#{nestedCol}" value="#{row.bindings[nestedCol.name].inputValue}"/> </af:column> </af:iterator> </f:facet> <f:facet name="ATTRIBUTE"> <af:dynamicComponent id="ad1" attributeModel="#{column}" value="#{row.bindings[column.name].inputValue}"/> </f:facet> </af:switcher> </af:column> </af:iterator> </af:table>
The value
attribute of the af:iterator
tag uses an EL expression that evaluates to the attributesModel.hierarchicalAttributes
property of the collection's tree binding. The attributesModel
property is used to retrieve the data object's attributes and their metadata, such as component type, label, tooltip, and other properties of the real component to be rendered. The hierarchicalAttributes
property signifies that a hierarchical list of displayable attributes and their metadata is provided, including any categories that have been set for any attributes in the UI hints.
The attributeModel
attribute of the af:dynamicComponent
tag is set to the EL expression #{column}
, which references the variable that is defined in the iterator's var
attribute and that serves as a pointer to the current column (or category) of the data control collection and its corresponding metadata.
What Happens at Runtime: How Attribute Values Are Dynamically Determined
When a page with dynamic components is rendered, the bindings are created just as they are when items are dropped from the Data Controls panel at design time, except that they are created at runtime. For more information, see What Happens at Runtime: How Attribute Values Are Dynamically Determined.
How to Create a Dynamic Table with a detailStamp Facet
You can create a dynamic table that displays master content and includes detailStamp
facet that enables a user to display detail for a given record. The detail content is obtained through the dynamic component's bindings to a view link accessor on the master view object. Figure 32-10 shows a part of a dynamic table that contains such a detail facet. For more information on using detailStamp
facets in tables, see Adding Hidden Capabilities to a Table in Developing Web User Interfaces with Oracle ADF Faces.
Figure 32-10 Dynamic Table with detailStamp Facet

Description of "Figure 32-10 Dynamic Table with detailStamp Facet"
Before you begin:
It may be helpful to have an understanding of dynamic tables. See Creating a Table with Dynamic Components.
You may also find it helpful to understand other ADF functionality and features. See Additional Functionality for Databound Tables.
You will need to complete these tasks:
-
Create an application module that contains instances of the view objects and view links that establish the desired master-detail hierarchy, as described in How to Create a Master-Detail Hierarchy Based on View Objects Alone.
-
Create a dynamic table as shown in How to Create a Dynamic Table.
To create a dynamic table with a detailStamp facet:
- In the source editor for the JSF page, insert a
f:facet
tag within the dynamic table'saf:table
tag. - Within the
f:facet
tag, insert anaf:panelFormLayout
tag. - Within the
af:panelFormLayout
tag, insert anaf:iterator
tag. - Bind the iterator's
value
attribute to theattributesModel.getLinkedViewAttributes(
LinkedViewName
)
property of your data object, whereLinkedViewName
is the name of a view link accessor that accesses the detail content. - Within the
af:iterator
tag, insert anaf:dynamicComponent
tag. - Bind the
af:dynamicComponent
tag'sattributeModel
attribute to the value of theaf:iterator
tag'svar
attribute. - Bind the
af:dynamicComponent
tag'svalue
attribute to an expression that returns the detail data for the selected row.
The following example shows the JSF page code for a dynamic table that uses a detailStamp
facet to display data retrieved through a view link accessor.
<af:table value="#{bindings.EmpVO3.collectionModel}" var="row" id="t1" ...> <f:facet name="detailStamp"> <af:panelForm id="pgl1" layout="vertical"> <af:iterator id="iter3" var="detail" value="#{bindings.EmpVO3.attributesModel.getLinkedViewAttributes('DeptView')}"> <af:dynamicComponent value="#{row['DeptView'].bindings[detail.name].inputValue}"/> attributeModel="#{detail}" id="dc1"/> </af:iterator> </af:panelForm> </f:facet> <af:iterator id="itr1" var="column" value="#{bindings.EmpVO3.attributesModel.attributes}"> <af:column headerText="#{column.label}" id="dcc1"> <af:dynamicComponent value="#{row.bindings[column.name].inputValue}" attributeModel="#{column}" id="dc1"/> </af:column> </af:iterator> </af:table>
Modifying the Attributes Displayed in the Table
You can modify and delete attributes displayed in a table that you don’t want to see in the UI.
Once you use the Data Controls panel to create a table, you can then delete attributes, change the order in which they are displayed, change the component used to display them, and change the attribute binding for the component. You can also add new attributes, or rebind the table to a new data control.
How to Modify the Displayed Attributes
You can modify the following aspects of a table that was created using the Data Controls panel:
-
Change the binding for the label of a column
-
Change the UI component bound to an attribute
-
Add or delete columns that represent the attributes
-
Reorder the columns in the table
-
Enable selection and sorting
Before you begin:
It may be helpful to have an understanding of modifying databound tables. For more information, see Modifying the Attributes Displayed in the Table.
You may also find it helpful to understand other ADF functionality and features. For more information, see Additional Functionality for Databound Tables.
You will need to complete this task:
- Create a databound table as described in Creating a Basic Table.
To change the attributes for a table:
- In the Structure window, select the table component.
- In the Properties window, expand the Columns section to change, add, or delete the attributes that are displayed as columns. You can also change the UI component used within the column to display the data.
- Expand other sections of the Properties window to change the display and behavior of the table, such as filtering and sorting.
How to Change the Binding for a Table
Instead of modifying a binding, you can completely change the object to which the table is bound.
Before you begin:
It may be helpful to have an understanding of modifying databound tables. For more information, see Modifying the Attributes Displayed in the Table.
You may also find it helpful to understand other ADF functionality and features. For more information, see Additional Functionality for Databound Tables.
You will need to complete this task:
-
Create a databound table as described in Creating a Basic Table.
To rebind a table:
Tip:
You can also rebind a table by dragging a different view object on top of the existing table.
What Happens When You Modify Bindings or Displayed Attributes
When you simply modify how an attribute is displayed by moving the UI component or changing the UI component, JDeveloper changes the corresponding code on the JSF page. When you use the binding editors to add or change a binding, JDeveloper adds the code to the JSF page, and also adds the appropriate elements to the page definition file.