Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers 10g Release 3 (10.1.3.0) Part Number B25947-02 |
|
|
View PDF |
This chapter describes how to use the Data Control Palette to create databound tables using ADF Faces components.
This chapter includes the following sections:
Section 14.4, "Modifying the Attributes Displayed in the Table"
Section 14.7, "Setting the Current Object Using a Command Component"
Unlike forms, tables allow you to display more than one data object from a collection at a time. Figure 14-1 shows the SRList page in the SRDemo application, which uses a browse table to display the current service requests for a logged in user.
Once you drop a collection as a table, you can then add row selection components that allow users to select a specific row. When you add command buttons bound to actions, users can then click those buttons to execute some logic on the selected row. For more information, see Section 17.3, "Creating Command Components to Execute Methods". You can also modify the default components to suit your needs.
Read this chapter to understand:
How to create a basic table
How to add navigation between sets of returned objects
How to modify the default table once it's created
How to add components that allow users to show or hide data
How to include a column that allows users to select one, or one or more, rows in the table
How to manually set the current row in the table
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 columns in the table 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.
To create a table using a data control, you bind the table
component to a view object collection. JDeveloper allows you to do this declaratively by dragging and dropping a collection from the Data Control Palette.
To create a databound table:
From the Data Control Palette, select a collection.
For example, to create the SRList table in the SRDemo application, you select the ServiceRequestsByStatus
collection that is under the LoggedInUser
collection. Figure 14-2 shows the ServiceRequestsByStatus
collection in the Data Control Palette.
The ServiceRequestsByStatus
collection, which extends ServiceRequests
, is a child of the LoggedInUser
collection because of the view link ServiceRequestsForUser
. The ServiceRequestsByStatus
collection also has a named bind variable StatusCode
that represents the service request status type (for example, open or pending requests). In the SRList page, when the logged in user selects a command link in the menu bar to view open, pending, closed, or all service requests, the requests created by or assigned to the currently logged in user for the selected status type are returned.
Drag the collection onto a JSF page, and from the context menu, choose the appropriate table.
When you drag the collection, you can choose from the following types of tables:
ADF Table: Allows you to select the specific attributes you wish your editable table columns to display, and what UI components to use to display the data. By default, each attribute on the collection object is displayed in an inputText
component, thus enabling the table to be editable.
ADF Read-Only Table: Same as the ADF Table; however, each attribute is displayed in an outputText
component.
ADF Read-Only Dynamic Table: The attributes returned and displayed are determined dynamically. This component is helpful when the attributes for the corresponding object are not known until runtime, or you do not wish to hardcode the column names in the JSF page. For example, if you have a polymorphic collection (for example, a view object collection that can be a collection of mammals or a collection of birds), the dynamic table can display the different attributes accordingly.
ADF Master Table, Inline Detail Table: For more information, see Section 15.6, "Using an Inline Table to Display Detail Data in a Master Table".
From the ensuing Edit Table Columns dialog, you can do the following:
Change the display label for a column. By default, the label is bound to the labels
property for the attribute on the table binding. For more information about the labels
property, see Appendix B, "Reference ADF Binding Properties". The bindings to the labels
property allow the labels to be inherited from the UI control hints that you have defined in your business domain layer, thus enabling you to change the value of a label text once in a central place, and have the change appear the same on all pages that display the label. In the Edit Table Columns dialog, you can instead enter text or an EL expression to bind the label value to something else, for example, a key in a resource file.
For example, the heading for the Status column in the table on the SRList page is bound to the labels
property that uses the Status
key to get the attribute:
#{bindings.LoggedInUserServiceRequests.labels.Status}
However, you could change the heading to instead be bound to a key in a properties resource file, for example:
#{srlist['sr.status']}
In the example, srlist
would be a variable defined in the JSF page used to load a properties file. For more information about using resource bundles, see Section 22.4, "Internationalizing Your Application".
Note that the SRDemo pages mainly use the inherited UI control hints for all attributes, and JSF resource strings for other kinds of labels that are not directly related to view object attributes.
Change the attribute binding for a column.
For example, you can change the status column to instead be bound to the requestDate
attribute. If you simply want to rearrange the columns, you should use the order buttons, as described later in the section. If you do change the attribute binding for a column, note the following:
If you change the binding, the label for the column also changes.
If you change the binding to an attribute currently bound to another column, the UI component changes to a component different from that used for the column currently bound to that attribute.
Change the UI component used to display an attribute. The UI components are either inputText
or outputText
and are set based on the table you selected when you dropped the collection onto the page. You can change to the other component using the dropdown menu. If you want to use a different component, such as a command link or button, you need to use this dialog to select the outputText
component, and then in the Structure window, replace the component with the desired UI component (such as a command link).
Change the order of the columns using the order buttons. Top moves the column to the first column at the left of the table. Up moves the column one column to the left. Down moves the column one to the right. Bottom moves the column to the very right.
Add a column using the New button. There's no limit to the number of columns you can add. When you first click New, JDeveloper adds a new column line at the bottom of the dialog and populates it with default values from the first attribute in the bound collection; subsequent new columns are populated with values from the next attribute in the sequence, and so on.
Delete a column using the Delete button. Doing so deletes the column from the table.
Add a tableSelectOne
component to the table's selection
facet by selecting Enable selection. For more information, see Section 14.6, "Enabling Row Selection in a Table".
Allow sorting for all columns by selecting Enable sorting.
Dropping a table from the Data Control Palette has the same effect as dropping a text field or form. For more information, see Section 13.2.2, "What Happens When You Use the Data Control Palette to Create a Text Field". 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.
When you drop a table from a the Data Control Palette, a table value binding is created. Like an attribute binding used in forms, the table value binding references the iterator binding; the iterator binding references an iterator for the data collection, which facilities iterating over the data objects in the collection. Instead of creating a separate binding for each attribute, only the table binding is created. In the table binding, the AttrNames
element contains a a child element for each attribute that you want to be available for display or reference in each row of the table. Example 14-1 shows the value binding for the table created when you drop the ServiceRequestsByStatus
collection.
Example 14-1 Value Binding Entries for a Table in the Page Definition File
<table id="LoggedInUserServiceRequests" IterBinding="ServiceRequestsByStatusIterator"> <AttrNames> <Item Value="SvrId"/> <Item Value="Status"/> <Item Value="RequestDate"/> <Item Value="ProblemDescription"/> <Item Value="ProdId"/> <Item Value="CreatedBy"/> <Item Value="AssignedTo"/> <Item Value="AssignedDate"/> </AttrNames> </table>
Only the table value binding is needed because only the table UI component needs access to the data. The table columns derive their information from the table binding.
When you use the Data Control Palette to drop a table onto a JSF page, JDeveloper creates a table that contains a column for each attribute on the object to which it is bound. To do this, 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 either an input
or outputText
component bound to the attribute's value. Each column's heading is bound to the labels
property for the attribute on the table binding. Example 14-2 shows a simplified code excerpt from the table on the SRList page.
Example 14-2 Simplified JSF Code for an ADF Faces Table
<af:table value="#{bindings.LoggedInUserServiceRequests.collectionModel}" var="row" ..> ... <af:column headerText="#{bindings.LoggedInUserServiceRequests.labels.Status}" sortProperty="Status" sortable="false"> <af:outputText value="#{row.Status}"/> </af:column> <af:column headerText="#{bindings.LoggedInUserServiceRequests.labels.RequestDate}" sortProperty="RequestDate" sortable="false"> <af:outputText value="#{row.RequestDate}"> <f:convertDateTime pattern="#{bindings.LoggedInUserServiceRequests.formats.RequestDate}"/> </af:outputText> </af:column> ... </af:table>
The table binding iterates over the data exposed by the iterator binding. The FacesCtrlRangeBinding
class extends the base JUCtrlRangeBinding
class to add specific methods to the base table binding object; one of the methods is the getCollectionModel
method, which the EL accesses using the collectionModel
property of the table binding. The table wraps the result set from the iterator binding in an oracle.adf.view.faces.model.CollectionModel
object. As the table binding iterates, it makes each item in the collection available within the table
component using the var
attribute.
In the example, the table iterates over the rows in the current range of the ServiceRequestsByStatusIterator
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.Status}"/>
Table 14-1 shows the other attributes defined by default for ADF Faces tables created using the Data Control Palette.
Table 14-1 ADF Faces Table Attributes and Populated Values
Attribute | Description | Default Value |
---|---|---|
|
Determines how may rows to display at one time. |
An EL expression that 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, displays No rows yet when no objects are returned. If the table is not viewable (for example if there are authorization restrictions set against the table), displays Access Denied. |
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 |
Additionally, a table may also have a selection
facet, and selection
and selectionListener
attributes if you chose to enable selection when you created your table. For more information, see Section 14.6, "Enabling Row Selection in a Table".
Instead of using built-in operations to perform navigation as forms do, ADF Faces tables provide built-in navigation using the selectRangeChoiceBar
component that is automatically included with table
components. The selectRangeChoiceBar
component renders a dropdown menu and Previous and Next links for selecting a range of records to display in the current page. Figure 14-3 shows an example of how the selectRangeChoiceBar
component might look like in a table.
The rows
attribute on a table
component determines the maximum number of rows to display in a range. When you use the Data Control Palette to create a table, by default JDeveloper sets the table to display a range of rows equal to the iterator's rangeSize
value, as shown in the following code snippet for the rows
attribute on the SRList table:
<af:table rows="#{bindings.LoggedInUserServiceRequests.rangeSize}".../>
The EL expression in the rows
attribute evaluates to the iterator's range size, which is defined in the page definition file:
<executables> <iterator id="ServiceRequestsByStatusIterator" RangeSize="10" Binds="ServiceRequestsByStatus" DataControl="SRService"/> </executables>
By default, the RangeSize
value is 10, which means that 10 records are returned at a time for display in the current page.
To change the number of records to return for display in the current page, edit the RangeSize
value in the page definition file (instead of editing directly the rows
attribute on the table
component).
If you change the rows
attribute on the table
component directly instead of changing the RangeSize
value on the iterator, make sure the value of rows
is equal to or less than the iterator's RangeSize
value. For additional information about the RangeSize
attribute, see Section 13.4.2.2, "Iterator RangeSize Attribute".
The selectRangeChoiceBar
component provides navigational links that allow a user to select the next and previous range of objects in the collection. If the total size of the collection is known, the component provides a dropdown menu that lets the user navigate directly to a particular range set in the collection (as illustrated in Figure 14-3). When you change the RangeSize
attribute on the iterator, the selectRangeChoiceBar
component automatically changes to show the new range sets.
The rows
attribute on a table
component is used in conjunction with the first
attribute to set the ranges. The first
attribute, which is a zero-based index of the rows in a range, determines the first row in the current range. By default, the rows
attribute uses an EL expression that binds its value to the value of the rangeSize
attribute of the associated iterator. The first
attribute also uses an EL expression, but the expression binds to the value of the iterator's rangeStart
attribute. For example, the rows
and first
attribute on the table on the SRList page have the following values:
<af:table rows="#{bindings.LoggedInUserServiceRequests.rangeSize}" first="#{bindings.LoggedInUserServiceRequests.rangeStart}"
Each current range starts with the row identified by first
, and contains only as many rows as indicated by the rows
attribute.
To determine the range sets for the selectRangeChoiceBar
to use, the view object retrieves the first "RangeSize" number of rows and then stops, and the table makes a separate SELECT COUNT(*) FROM (...)
query by calling the getEstimatedRowCount()
method, which estimates the total number of rows the query would retrieve without actually retrieving them all. For more information about the getEstimatedRowCount()
method, see Section 5.6.2, "Counting the Number of Rows in a RowSet".
Unlike navigation operations which rely on logic in an action binding to provide navigation, the selectRangeChoiceBar
component sends a RangeChangeEvent
event. When a user navigates to a different range by selecting one of the navigation links provided by the selectRangeChoiceBar
component, (such as Previous or Next), the table generates a RangeChangeEvent
event. This event includes the index of the object that should now be at the top of the range. The table responds to this event by changing the value of the first
attribute to this new index.
The RangeChangeEvent
event has an associated listener. You can bind the RangeChangeListener
attribute on the table to a method on a managed bean. This method will then be invoked in response to the RangeChangeEvent
event, in other words whenever the table has changed the first
attribute in response to the user changing a range on the table. This binding can be helpful when some complementary action needs to happen in response to user navigation, for example, if you need to release cached data created for a previous range. For information about adding logic before or after built-in operations, see Section 17.5, "Overriding Declarative Methods".
Note that using the browser Back button has the same issues as described in Chapter 13. For more information, see Section 13.4.4, "What You May Need to Know About the Browser Back Button". Because the iterator keeps track of the current object, when a user clicks a browser's Back button instead of using navigation buttons on the page, the iterator becomes out of sync with the page displayed because the iterator has been bypassed. Like in forms, in tables the current row (or range or rows) displayed in the page you see when you use the browser Back button may no longer correspond with the iterator binding's notion of the current row and range.
For example, in the SRList page shown in Figure 14-1, if you select the service request with the ID of 4
and then navigate off the page using either the ID's link or the View or Edit buttons, the iterator is set to the object that represents service request 4. If you set EnableTokenValidation
to be true
(as described in the procedure in Section 13.4.4, "What You May Need to Know About the Browser Back Button"), then the page's token is also set to 4. When you use the browser's Back button, everything seems to be fine, the same range is displayed. However, if you click another button, an error indicating that the current row is out of sync is shown. This is because the page displayed is the previous page, whose token was set to 0, while the iterator is at 4.
Once you use the Data Control Palette 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. Before you add new attributes, make sure the table binding includes the attribute you want to display in the table.
You can modify the following aspects of a table that was created using the Data Control Palette.
Change the binding for the label of a column
Change the attribute to which a UI component is bound
Change the UI component bound to an attribute
Reorder the columns in the table
Delete a column in the table
Add a column to the table
To change the attributes for a table:
In the Structure window, right-click af:table and choose Edit Columns.
In the Edit Table Columns dialog, you can do the following:
Change the display label for the column. By default, the label is bound to the labels
property for the attribute on the table binding. For more information about the labels
property, see Appendix B, "Reference ADF Binding Properties". The bindings to the labels
property allow the labels to be inherited from the UI control hints that you have defined in your business domain layer, thus enabling you to change the value of a label text once in a central place, and have the change appear the same on all pages that display the label. In this dialog, you can instead enter text or an EL expression to bind the label value to something else, for example, a key in a resource file.
For example, the heading for the Status column in the table on the SRList page is bound to the labels
property that uses the Status
key to get the attribute:
#{bindings.LoggedInUserServiceRequests.labels.Status}
However, you could change it to instead be bound to a key in a properties resource file, for example:
#{srlist['sr.status']}
In this example, srlist
would be a variable defined in the JSF page used to load a properties file. For more information about using resource bundles, see Section 22.4, "Internationalizing Your Application".
Change the attribute binding for a column.
For example, you can change the Status column to instead be bound to the RequestDate
attribute. Note the following:
If you change the binding, the label for the column also changes.
If you change the binding to an attribute currently bound to another column, the UI component changes to a component different from that used for the column currently bound to that attribute.
If you simply want to rearrange the columns, you should use the order buttons, as described later in the section.
Change the UI component used to display the attribute. The UI components are either inputText
or outputText
and are set based on the composite component you selected when you dropped the collection onto the page. You can change to the other UI component using the dropdown menu. If you want to use an entirely different component, such as a command link or button, you need to use this dialog to change to an outputText
component, and then in the Structure window, replace the outputText
component with the desired UI component (such as a command link).
Tip:
You can use the following UI components in a table with the noted caveats:The selectBooleanCheckbox
component can be used inside a table if it is only handling boolean
or java.lang.Boolean
types.
The selectOneListbox/Choice/Radio
components can be used inside the table if you manually add the list of choices as an enumeration. If instead you want to use a list binding, then the selectOne
UI component cannot be used inside a table. For more information on list bindings, see Section 19.7, "Creating Selection Lists".
Change the order of the columns using the order buttons. Top moves the column to the first column at the left of the table. Up moves the column one column to the left. Down moves the column one to the right. Bottom moves the column to the very right.
Add a column using the New button. Doing so adds a new column at the bottom of the dialog and populates it by default with values from the next sequential attribute in the collection. You then need to edit the values. You can only select an attribute associated with the object to which the table is bound.
Delete a column using the Delete button. Doing so deletes the column from the table.
Add a tableSelectOne
component to the table's selection
facet by selecting Enable selection. For more information, see Section 14.6, "Enabling Row Selection in a Table".
Add sorting capabilities by selecting Enable sorting.
Instead of modifying a binding, you can completely change the object to which the table is bound.
To rebind a table:
Right-click the table in the Structure window and choose Edit Binding to launch the Table Binding Editor.
In the editor, select the new collection to which you want to bind the table. Note that changing the binding for the table will also change the binding for all the columns. You can then use the procedures in Section 14.4.1, "How to Modify the Displayed Attributes" to modify those bindings.
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.
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 labeled Details with a toggle. When the user activates the toggle, the component added to the facet is shown. When the user clicks on the toggle again, the component is hidden. For more information about facets in general, see Section 13.3.2.1, "Using Facets". Figure 14-4 shows how the description of a service request in an outputText
component can be hidden or shown in the table (note that this functionality does not currently exist in the SRDemo application).
If you wish to show details of another object that has a master-detail relationship (for example, if you wanted to show the details of the person to whom the service request is assigned), you could use the Master Table-Inline Detail
composite component. For more information about master-detail relationships and the use of the master-detail composite component, see Section 15.6, "Using an Inline Table to Display Detail Data in a Master Table".
To use the detailStamp
facet, you insert a component that is bound to the data to be displayed or hidden into the facet. You can also set an attribute on the table that creates a link that allows a user to show or hide all details at once.
To use the detailStamp facet:
From the Data Control Palette, drag the attribute to be displayed in the facet onto the detailStamp facet folder. Figure 14-5 shows how the detailStamp facet folder appears in the Structure window.
From the ensuing context menu, choose the UI component to display the attribute.
If you want a link to allow users to hide or show all details at once, select the table in the Structure window. Then in the Property Inspector, set the allDetailsEnabled
attribute to true
.
If the attribute to be displayed is specific to a current record, then you need to replace the JSF code (which simply binds the component to the attribute), so that it uses the table's variable to display the data for the current record.
For example, when you drag an attribute, JDeveloper inserts the following code:
<f:facet name="detailStamp"> <af:outputText value="#{bindings.<attributename>.inputValue}"/> </f:facet>
You need to change it to the following:
<f:facet name="detailStamp"> <af:outputText value="#{row.<attributename>}"/> </f:facet>
When you drag an attribute in the detailStamp
facet folder, JDeveloper adds the attribute value binding to the page definition file if it did not already exist, and it also adds the code for facet to the JSF Page.
For example, say on the SRList page you want the user to be able to optionally hide the service request description. Since the table was created using the ServiceRequestsByStatus
collection, you can drag the ProblemDescription
attribute and drop it inside the detailStamp facet folder in the Structure window.
Example 14-3 shows the code JDeveloper then adds to the JSF page.
Example 14-3 JSF Code for a detailStamp Facet
<f:facet name="detailStamp"> <af:outputText value="#{bindings.ServiceRequestsByStatusProblemDescription.inputValue}"/> </f:facet>
You then need to change the code so that the component uses the table's variable to access the correct problem description for each row. Example 14-4 shows how the code should appear after using the row
variable.
When the user hides or shows the details of a row, the table generates a DisclosureEvent
event (or a DisclosureAllEvent
event when the allDetailsEnabled
attribute on the table is set to true
). The event tells the table to toggle the details (that is, either expand or collapse).
The DisclosureEvent
event has an associated listener. You can bind the DisclosureListener
attribute on the table to a method on a managed bean. This method will then be invoked in response to the DisclosureEvent
event to execute any needed post-processing.
When the tableSelectOne
component or the tableSelectMany
component is added to the table's selection
facet, the table displays a Select column that allows a user to select one row, or one or more rows, and then take some action on those rows via command buttons.
The tableSelectOne
component allows the user to select just one row. This component provides a radio button for each row in the Select column, as shown in Figure 14-6. For example, the table in the SRList page has a tableSelectOne
component that allows a user to select a row, and then click either the View or Edit command button to view or edit the details for the selected service request.
The tableSelectMany
component displays a checkbox for each row in the Select column, allowing the user to select one or more rows. When you use the tableSelectMany
component, text links are also added that allow the user to select all or none of the rows, as shown in Figure 14-7. For example, the table on the SRMain page has a tableSelectMany
component that allows a user to select multiple records, and then click the Delete Service History Record command button to delete the selected records.
Both table row selection components have a text
attribute whose value can be instructions for the user. The table row selection components also usually have command button or command links as children, which are used to perform some action on the selected rows. For example, the table on the SRList page has command buttons that allows a user to view or edit the selected service request.
You can set the required
attribute on both the tableSelectOne
and the tableSelectMany
components to true
. This value will cause an error to be thrown if the user does not select a row. However, if you set the required
attribute, you must also set the summary
attribute on the table in order for the required input error message to display correctly. For more information about the required
attribute, see Section 20.3.1.1.1, "Using Validation Attributes".
You can also set the autoSubmit
attribute on the tableSelectOne
and the tableSelectMany
components. When the autoSubmit
attribute is set to true
, the form that holds the table automatically submits when the user makes a selection. For more information, see Section 11.6, "Best Practices for ADF Faces".
The procedures for using the tableSelectOne
and tableSelectMany
are quite different. In ADF applications, operations (such as methods) work on the current data object, which the iterator keeps track of. The tableSelectOne
component is able to show the current data object as being selected, and is also able to set a newly selected row to the current object on the iterator. If the same iterator is used on a subsequent page (for example, if the user selects a row and then clicks the command button to navigate to a page where the object can be edited), the selected object will be displayed. This works because the iterator and the component are working with a single object; the notion of the current row is the same because the different iterator bindings in different binding containers are bound to the same row set iterator.
However, with the tableSelectMany
component, there are multiple selected objects. The ADF Model layer has no notion of "selected" as opposed to "current." You must add logic to the model layer that loops through each of the selected objects, making each in turn current, so that the operation can be executed against that object.
When you drop a collection from the Data Control Palette as a table, you have the choice to include the selection
facet. If you select Enable selection, a tableSelectOne
component is inserted into the selection
facet, along with a Submit commandButton
component as a child of tableSelectOne
.
Note:
You cannot insert atableSelectMany
component when you create a table using the Data Control Palette. You need to manually add it after creating the table. Note however, that you must create additional code in order to use multi-select processing in an ADF application. For more information, see Section 14.6.5, "How to Use the TableSelectMany Component in the Selection Facet".If you wish to have the Submit button bound to a method, you need to rebind the commandButton
component to the method or operation of your choice. For rebinding procedures, see Section 21.6, "Adding ADF Bindings to Actions".
You can also manually add a tableSelectOne
component to a selection
facet.
To manually use the selection facet:
In the Structure window, select af:table and choose Edit Columns from the context menu.
In the Edit Table Columns dialog, select Enable selection and click OK.
JDeveloper adds the tableSelectOne
component to the selection facet folder (plus the needed listener and attribute that work with selection on the table
component).
In the Structure window, expand the table's selection facet folder and select af:tableSelectOne.
In the Property Inspector for the new component, enter a value for the text
attribute that will provide instructions for using any command buttons or links used to process the selection.
(Optional): Rebind the Submit command button to a method or operation of your choice from the Data Control Palette. For rebinding procedures, see Section 21.6, "Adding ADF Bindings to Actions". For more information about using methods to create command buttons, see Section 17.3, "Creating Command Components to Execute Methods".
Note:
Until you add a command component to the facet, the value for thetext
attribute will not display.As Example 14-5 shows, when you elect to enable selection as you first create or later edit a table, the tableSelectOne
component is inserted into the selection
facet with Select and
as the value for the text
attribute. A Submit command button is also included as a child.
Example 14-5 Selection Facet Code
<f:facet name="selection"> <af:tableSelectOne text="Select and"> <af:commandButton text="Submit"/> </af:tableSelectOne>
As Example 14-6 shows, the table's selectionState
attribute's value is an EL expression that evaluates to the selected row on the collection model created from the iterator. The selectionListener
attribute's value evaluates to the makeCurrent
method on the collection model. This value is what allows the selection facet to set the selected row as the current object on the iterator.
Example 14-6 Selection Attributes on a Table
<af:table var="row" rows="#{bindings.ServiceRequests.rangeSize}" first="#{bindings.ServiceRequests.rangeStart}" selectionState="#{bindings.ServiceRequests.collectionModel.selectedRow}" selectionListener="#{bindings.ServiceRequests.collectionModel.makeCurrent}"...>
Once the user makes a selection and clicks the associated command button, the tableSelectOne
component updates the RowKeySet
obtained by calling the getSelectionState()
method on the table. Since the selection state evaluates to the selected row on the collection model, that row is marked as selected. This selection is done prior to calling the ActionListener
associated with the command button.
For a tableSelectOne
component, because the current row is selected before the ActionListener
is invoked, you can bind the ActionListener
on the command button to a method on a managed bean that provides the corresponding processing on the data in the row. Or you can simply add the logic to the declarative method. For more information, see Section 17.5, "Overriding Declarative Methods".
The tableSelectOne
component triggers a SelectionEvent
event when the selection state of the table is changed. The SelectionEvent
reports which rows were selected and deselected. Because the SelectionListener
attribute is bound to the makeCurrent
method on the collection model, this method is invoked when the event occurs, and sets the iterator to the new current row.
As described in Section 14.6.1, "How to Use the TableSelectOne Component in the Selection Facet", a commandButton
component is automatically added as a child of the tableSelectOne
component when you drop a collection as a table and select Enable selection at the same time, or when you use the Edit Table Columns dialog to enable selection later. The tableSelectOne
component is inserted into the selection
facet on the table
component.
When the user makes a selection in the table, the tableSelectOne
component sets the selected row as the current object on the iterator. Then the user can perform some action on the selected row via the command button.
Instead of using the selection
facet components to set the current object and providing a commandButton
to navigate to the next page, you can use a commandLink
component that lets the user click a link to both perform an operation on a selection and navigate to another page. As shown in Figure 14-6, the second column of links in the table saves the user the step of having to first select a row and then click a command button to perform an action and navigate. If you add column links, you must manually set the current object on the iterator binding. For more information about manually setting the current object, see Section 14.7, "Setting the Current Object Using a Command Component".
Tip:
If the subsequent page does not use the same iterator, you will most likely have to manually set the parameter that represents the selected row for the subsequent page. For example, from the SRList page, when the user selects a service request and then clicks the Edit command button, the application navigates to the SREdit page, which displays the correct data for the selected service request. The Edit command button, which uses thesetCurrentRowWithKey
action binding, includes the af:setActionListener
tag to set the appropriate value into processScope
before navigation. The SREdit page has an invokeAction
object that invokes the setCurrentRowWithKey
operation; the value bound to the rowKey
NamedData
element is passed in as the parameter, which determines the current row to display. For more information, see Section 17.4, "Setting Parameter Values Using a Command Component".You cannot insert a tableSelectMany
component when you create a table using the Data Control Palette. You need to manually add it after creating the table.
Unlike the tableSelectOne
component, with the tableSelectMany
component, there are multiple selected objects.
When you add the tableSelectMany
component to a table that uses an ADF table binding, you need to pass the set of selected Keys to a method that processes each one in turn.
To use the tableSelectMany component in an ADF application:
Create the table as shown in Section 14.2.1, "How to Create a Basic Table" but do not select Enable selection.
In the Structure window, expand the Table facets folder, right-click the selection facet folder, and choose Insert inside selection > TableSelectMany.
In the Structure window, select the af:table node and in the Property Inspector, delete the values for the SelectionState and SelectionListener attributes, if necessary. Doing so will keep the component from setting one of the selected rows to the current object, as you need this logic to be handled through the code you create.
From the Data Control Palette, drag the method that will operate on the selected objects on top of the af:tableSelectMany node. From the ensuing context menu, choose Create > ADF Command Button. Doing so drops the method as a commandButton
component. You need to set the parameter value for the method, if the method accepts parameters.
For example, to create the SRMain page where a user can delete multiple service history records associated with a service request, you would drag the custom deleteServiceHistoryNotes(Set)
operation onto the af:tableSelectMany node.
In the Action Binding Editor, enter the value for the parameter by clicking the ellipses button (...) in the Value field to open the EL Expression Builder. Select the node that represents the value for the parameter.
For example, the table on the SRMain page is bound to the historyTable
property on the managed bean named backing_SRMain
. To access the set of selected rows in the table, you would use the following as the value for the parameter:
#{backing_SRMain.historyTable.selectionState.keySet}
For more information, see Section 17.3.1, "How to Create a Command Component Bound to a Service Method".
Add logic to allow the declarative method to operate against the set of selected rows. To add the logic, you override the declarative method created when the command button was dropped. For instructions, see Section 17.5, "Overriding Declarative Methods". Briefly, you override the declarative method by binding the command button's action
attribute to a backing bean method that has the added logic. Example 14-11 shows the backing bean method onDeleteHistoryRows()
created for the SRMain page.
When you insert the tableSelectMany
component into a table, and then add a command button bound to a service method, JDeveloper does the following:
Adds the tableSelectMany
and commandButton
components to the selection
facet on the table
component
Creates a method binding for the bound method in the page definition file, including a NamedData
element to hold the value of the parameter needed for the method (if any), determined when you dropped the method as a button
Example 14-7 shows the code for the table
, tableSelectMany
, and commandButton
components.
Example 14-7 JSF Code for a Table That Uses the TableSelectMany Component
<af:table value="#{bindings.ServiceHistories.collectionModel}" var="row" rows="#{bindings.ServiceHistories.rangeSize}" first="#{bindings.ServiceHistories.rangeStart}"... binding="#{backing_SRMain.historyTable}"> <af:column headerText="#{bindings.ServiceHistories.labels.SvhDate}" sortable="false"> <af:outputText value="#{row.SvhDate}"> <f:convertDateTime pattern="#{bindings.ServiceHistories.formats.SvhDate}"/> </af:outputText> </af:column> <af:column headerText="#{bindings.ServiceHistories.labels.SvhType}" sortable="false"> <af:outputText value="#{row.SvhType}"/> </af:column> <af:column headerText="#{bindings.ServiceHistories.labels.Notes}" sortable="false"> <af:outputText value="#{row.Notes}"/> </af:column> <f:facet name="selection"> <af:tableSelectMany text="Select items and ..."> <af:commandButton text="..." actionListener="#{bindings.deleteServiceHistoryNotes.execute}" .../> </af:tableSelectMany> </f:facet> </af:table>
When you create a command button using a custom service method, JDeveloper binds the button to the method using the actionListener
attribute. The button is bound to the execute
property of the action binding for the given method. Example 14-8 shows the page definition code that contains the method action binding for the bound method on the command button.
Example 14-8 DeleteServiceHistoryNotes Method Action Binding
<methodAction id="deleteServiceHistoryNotes" InstanceName="SRService.dataProvider" DataControl="SRService" MethodName="deleteServiceHistoryNotes" RequiresUpdateModel="true" Action="999"> <NamedData NDName="keySet" NDValue="${backing_SRMain.historyTable.selectionState.keySet}" NDType="java.util.Set"/> </methodAction>
The method action binding causes the associated method to be invoked on the business service, passing in the value bound to the NamedData
element as the parameter.
Instead of binding the command button to the execute
method on the action binding, you can bind the button to a method in a backing bean that overrides the declarative method. Doing so allows you to add logic before or after the original method runs. When you override a declarative method, JDeveloper automatically rebinds the command component to the new method using the action
attribute (instead of the actionListener
attribute), as shown in Example 14-9.
Example 14-9 Command Button Code After You Override the Declarative Method
<af:commandButton text="..." action="#{backing_SRMain.onDeleteHistoryRows}"/>
Example 14-10 shows the binding container code that JDeveloper inserts into the backing bean when you override the declarative method. The code accesses the binding container and finds the binding for the associated method.
Example 14-10 Binding Container Code Added by JDeveloper in the Backing Bean
private BindingContainer bindings; public BindingContainer getBindings() { return this.bindings; } public void setBindings(BindingContainer bindings) { this.bindings = bindings; } public String onDeleteHistoryRows() { BindingContainer bindings = getBindings(); OperationBinding operationBinding = bindings.getOperationBinding("deleteServiceHistoryNotes"); Object result = operationBinding.execute(); if (!operationBinding.getErrors().isEmpty()) { return null; } return null; }
By adding logic before or after the execute()
call on the method binding, you can perform needed logic before or after the declarative method.
Like the tableSelectOne
component, when the user makes multiple selections with the tableSelectMany
component and then clicks the associated command button, the tableSelectMany
component updates the selection state of the table by placing all the rows selected by the user in a RowKeySet
.
Example 14-11 shows the SRMain page backing bean method that overrides the declarative method created when the deleteServiceHistoryNotes(Set)
operation is dropped as a command button.
Example 14-11 Backing Bean Method for Deleting Service History Records
public String onDeleteHistoryRows() { BindingContainer bindings = getBindings(); Set keySet = getHistoryTable().getSelectionState().getKeySet(); if (!keySet.isEmpty()) { getBindings().getOperationBinding("deleteServiceHistoryNotes").execute(); keySet.clear(); } return null; }
Example 14-12 shows the code in the backing bean that you need to add for working with the service history table.
Example 14-12 Backing Bean Code for Working with the Service History Table
private CoreTable historyTable; public void setHistoryTable(CoreTable historyTable) { this.historyTable = historyTable; } public CoreTable getHistoryTable() { return historyTable; }
When the user selects multiple rows in the service history table, and then clicks the Delete Service History Record button, the tableSelectMany
component updates the selection state of the table by placing all the selected rows in a RowKeySet
. The backing bean method onDeleteHistoryRows()
accesses the binding container, and retrieves the selected keys by calling getSelectionState.getKeySet()
on the history table. The backing bean method then executes the deleteServiceHistoryNotes
method action binding (which is bound to the deleteServiceHistoryNotes()
method in the application module's client interface) by calling the execute()
method on the binding. The service method loops through the keySet
, deleting the row found by each key. Finally, the backing bean method calls the clear()
method on the keySet
to remove the keys, after the service method has deleted all the selected rows.
There may be cases where you need to programmatically set the current row for an object on an iterator. For example, the SRList page in the SRDemo application uses command links in the second column, as shown in Figure 14-8, which the user can click to directly edit a service request, without needing to first select the row.
While using command links saves a step for the user, command links do not offer the same functionality as the selection
facet, in that they can neither determine nor set the current row on the iterator. Therefore, you must manually set the current row.
You use the setCurrentRowWithKey
or setCurrentRowWithKeyValue
built-in operations to set the current row. These operations are built-in methods on any iterator for a collection. The setCurrentRowWithKey
operation allows you to set the current row given "stringified" key. The setCurrentRowWithKeyValue
operation allows you to set the current row given the a primary key's value. For more information about the current row operations, see Section 10.5.6, "Understanding the Difference Between setCurrentRowWithKey and setCurrentRowWithKeyValue".
While you can drop these operations as any type of command component, the commandLink
component is most usually used in this situation. The following procedure explains how to use this component with the setCurrentRowWithKey
and setCurrentRowWithKeyValue
operations.
From the Data Control Palette, drag the setCurrentRowWithKey
or setCurrentRowWithKeyValue
operation.
From the context menu, choose Operations > ADF Command Link.
JDeveloper creates the command link component on the page, and adds the action binding to the page definition file. You need to change the value for the rowKey
parameter in the Action Binding Editor.
Select the command link component in the Structure window, and choose Edit Binding from the context menu.
In the Action Binding Editor, by default, the value for the rowKey
parameter is set to ${bindings.setCurrentRowWithKey_rowKey}
. The actual value should be something that can be used to determine the current row.
For example, the command link in Figure 14-8 needs to set the current row to the same row as the link being clicked. To access the "stringified" key of the row for the setCurrentRowWithKey
operation, you can use the rowKeyStr
property on the binding, or #{row.rowKeyStr}
.
Alternatively, if you use the setCurrentRowWithKeyValue
operation, you might set the rowKey
to the value of the current row, or #{row.svrId}
For more information about the variable used to set the current row on a table (in this case, row
), see Section 14.2.2.2, "Code on the JSF Page for an ADF Faces Table".
When you use the setCurrentRowWithKey
operation as a command component, JDeveloper creates an action binding for that operation. Because this operation takes a parameter (rowKey
) to determine the current row, it has a NamedData
element used to set that value (for more information about parameters and the NamedData
element, see Section 17.3, "Creating Command Components to Execute Methods").
Example 14-13 shows the code created in the page definition file when you drop the setCurrentRowWithKey
operation and set #{row.rowKeyStr}
as the value for the rowKey
parameter.
Example 14-13 Page Definition Code for the SetCurrentRowWithKey Operation
<action id="setCurrentRowWithKey" IterBinding="ServiceRequestsByStatusIterator" InstanceName="SRService.ServiceRequestsByStatus" DataControl="SRService" RequiresUpdateModel="false" Action="96"> <NamedData NDName="rowKey" NDValue="${row.rowKeyStr}" NDType="java.lang.String"/> </action>
When a user clicks the command link, the setCurrentRowWithKey
operation is executed on the iterator, using the rowKey
parameter to determine the current row. As with the tableSelectOne
component, if you use the same iterator to display the current record, the correct data will display.
Tip:
For functionality similar to that in the SRDemo application, you may need your command link to pass a parameter value that represents the current row. This value might be used by the method used to create the ensuing form. For more information and procedures, see Section 17.4, "Setting Parameter Values Using a Command Component".