Skip Headers
Oracle® Application Development Framework Developer's Guide
10g Release 3 (10.1.3.0)

Part Number B28967-02
Go to Documentation Home
Home
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

7 Adding Tables

This chapter describes how to use the Data Control Palette to create databound tables using ADF Faces components.

This chapter includes the following sections:

7.1 Introduction to Adding Tables

Unlike forms, tables allow you to display more than one data object from a collection at a time. Figure 7-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.

Figure 7-1 The Service Request Table

The SRList page contains a table that lists service requests

Once you drop a collection as a table, you can then add selection components that allow users to select a specific row. When you add command buttons bound to methods, users can then click those buttons to execute some business logic on the selected row. For more information, see Section 10.3, "Creating Command Components to Execute Methods". You can also modify the default components to suit your needs.

Read this chapter to understand:

7.2 Creating a Basic 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.

7.2.1 How to Create a Basic Table

To create a table using a data control, you must bind to a method on the data control that returns a collection. JDeveloper allows you to do this declaratively by dragging and dropping a collection from the Data Control Palette.

To create a databound table:

  1. From the Data Control Palette, select a method return that returns a collection.

    For example, to create the SRList table in the SRDemo application, you drag the ServiceRequest collection that the findServiceRequest(Integer, String) method returns. This method takes an Integer parameter value that represents the user ID of the current user and a String value that represents the status of open, and returns all open requests for that user. Figure 7-2 shows the ServiceRequests collection in the Data Control Palette. For more information about how the parameters are set to determine the records to display, see Section 10.6.1, "How to Create a Form or Table Using a Method That Takes Parameters".

    Figure 7-2 Collection Returned for a Method

    Service Request collection returned by findServiceRequests
  2. Drag the method return 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 method that returns a polymorphic collection (i.e. getAnimals() can return a collection of mammals or a collection of birds), the dynamic table can display the different attributes accordingly.

      Note:

      You can also drop a collection as an ADF Master Table, Inline Detail Table. For more information, see Section 8.6, "Using an Inline Table to Display Detail Data in a Master Table".
  3. 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 label property of the table binding. For more information about this property, see Section B, "Reference ADF Binding Properties". This binding to the property allows you to change the value of the label text once and have it 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 headings for the status columns in the table on the SRList page are bound to the label property of the status attribute binding:

      #{bindings.findServiceRequests1.labels.status}
      

      However, you could change the headings to instead be bound to a key in a properties 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 14.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. See the fourth bullet point below for more information.

    • 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, add that other UI component (such as a command link) as a parent to this component.

    • 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 7.6, "Enabling Row Selection in a Table".

    • Allow sorting for all columns by selecting Enable sorting.

      Note:

      If you choose to enable sorting, the table can only sort through the number of objects returned by the iterator, as determined by the iterators rangeSize attribute.

7.2.2 What Happens When You Use the Data Control Palette to Create a Table

Dropping a table from the Data Control Palette has the same effect as dropping a text field or form. For more information, see Section 6.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.

7.2.2.1 Iterator and Value Bindings for Tables

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. However, instead of creating a separate binding for each attribute, only the table binding is created. This table binding has a child attribute name element for each attribute. Example 7-1 shows the table binding for the table created when you drop the ServiceRequest collection.

Example 7-1 Value Binding Entries for a Table in the Page Definition File

<table id="findServiceRequest1" IterBinding="findServiceRequestsIter">
  <AttrNames>
    <Item Value="svrId"/>
    <Item Value="status"/>
    <Item Value="requestDate"/>
    <Item Value="problemDescription"/>
    <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.

7.2.2.2 Code on the JSF Page for an ADF Faces Table

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. This component 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 attribute is bound to the label property for each attribute on the table binding. Example 7-2 shows a simplified code excerpt from the table on the SRList page.

Example 7-2 Simple Example of JSF Code for an ADF Faces Table

<af:table var="row"
              value="#{bindings.findServiceRequests1.collectionModel}
  <af:column headerText="#{bindings.findServiceRequests1.labels.svrId}"
      <af:outputText value="#{row.svrId}"/>
  </af:column>
  <af:column headerText="#{bindings.findServiceRequests1.labels.status}"
    <af:outputText value="#{row.status}"/>
  </af:column>
         ...   
</af:table>

An ADF Faces table itself iterates over the data accessed by the iterator binding. In order to do this, the table wraps the result set from the iterator binding in an oracle.adf.view.faces.model.CollectionModel object. As the table iterates, it makes each item in the collection available within the table component using the var attribute.

In the example above, the table iterates over the collection from the findServiceRequests1 table binding, which in turn references the findServiceRequestsIter iterator binding. The iterator binding is what determines the current data object. 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 outputText tag:

<af:outputText value="#{row.status}"/>

Table 7-1 shows the other attributes defined by default for ADF Faces tables created using the Data Control Palette.

Table 7-1 ADF Faces Table Attributes and Populated Values

Attribute Description Default Value

rows

Determines how may rows to display at one time.

An EL expression that evaluates to the rangeSize property of the associated iterator binding. For more information on this attribute, see Section 7.3, "Incorporating Range Navigation into Tables". Note that the value of the rows attribute is equal to or less than the corresponding iterator's rangeSize value.

first

Index of the first row in a range (based on 0).

An EL expression that evaluates to the rangeStart property of the associated iterator binding. For more information on this attribute, see Section 7.3, "Incorporating Range Navigation into Tables".

emptyText

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

   

sortProperty

Determines the property on which to sort the column.

Set to the columns corresponding attribute binding value.

sortable

Determines whether a column can be sorted

Set to false. When set to true, the table will sort only the rows returned by the iterator.


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 7.6, "Enabling Row Selection in a Table".

7.3 Incorporating Range Navigation into Tables

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 7-3 shows an example of how the selectRangeChoiceBar component might look like in a table.

Figure 7-3 SelectRangeChoiceBar in a Table

SRList table can navigate through sets of service requests

7.3.1 How to Use Navigation Controls 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:

#{bindings.findServiceRequests1.rangeSize}

You can modify the rows attribute to display a different range size. For example, you may want the iterator to return 50 records, but you want the table to display only 5 at a time. However, if you plan on displaying the same amount you are retrieving, instead of changing the table's range size, you should keep this attribute bound to the iterator's range size, and then change the iterator. For more information, see Section 6.4.2.2, "Iterator RangeSize Attribute".

To change the table's range size:

  1. Select the table in the Structure window.

  2. In the Property Inspector, for the rows attribute, enter a value for the number of rows to display at a time.

    Alternatively, you can manually set the rows attribute in the JSF code:

    <af:table rows="5">
    

    WARNING:

    The value of the rows attribute must be equal to or less than the corresponding iterator's rangeSize value.

7.3.2 What Happens When You Use Navigation Controls in a Table

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 7-3). When you change the RangeSize attribute on the iterator, the selectRangeChoiceBar component automatically changes to show the new range sets.

You use the rows attribute on a table component in conjunction with the first attribute to set the ranges. The first attribute determines the current range to display. This attribute is an index (based at zero) of each row in the list. 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 uses an EL expression that binds its value 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.findServiceRequests1.rangeSize}"
          first="#{bindings.findServiceRequests1.rangeStart}"

Each range starts with the row identified by first, and contains only as many rows as indicated by the rows attribute.

7.3.3 What Happens at Runtime

When the total number of data objects in the collection exceeds the value of the rows attribute, the table displays the selectRangeChoiceBar component, which allows the user to navigate through the row sets.

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 10.5, "Overriding Declarative Methods".

7.3.4 What You May Need to Know About the Browser Back Button

Note that using the browser Back button has the same issues as described in Chapter 6. For more information, see Section 6.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 7-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 6.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.

7.4 Modifying the Attributes Displayed in the Table

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.

7.4.1 How to Modify the Displayed Attributes

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:

  1. In the Structure window, right-click af:table and choose Edit Columns.

  2. In the Edit Columns dialog, you can do the following:

    • Change the label for the column. By default, the label is bound to the label property of the table binding. For more information about this property, see Appendix B, "Reference ADF Binding Properties". This binding allows you to change the label once and have it 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 headings for the status columns in the table on the SRList page are bound to the label property of the status attribute binding:

      #{bindings.findServiceRequests1.labels.status}
      

      However, you could change it to instead be bound to a key in a properties 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 14.4, "Internationalizing Your Application".

    • Change the attribute binding for the 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 widget 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 change to an outputText component, and then in the Structure window, add that other UI component (such as a command link) as a parent to this component.

      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.Beoolean types.

      • The selectOneList/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 11.7, "Creating Databound Dropdown 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 7.6, "Enabling Row Selection in a Table".

    • Add sorting capabilities by selecting Enable sorting.

      Note:

      If you choose to enable sorting, the table can only sort through the number of objects returned by the iterator, as determined by the iterators rangeSize attribute.

7.4.2 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.

To rebind a table:

  1. Right-click the table in the Structure window and choose Edit Binding to launch the Table Binding Editor.

  2. 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 7.4.1, "How to Modify the Displayed Attributes" to modify those bindings.

7.4.3 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.

7.5 Adding Hidden Capabilities to a Table

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 6.3.2.1, "Using Facets". Figure 7-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).

Figure 7-4 Table with an Output UI Component in the DetailStamp Facet

Problem description text can be hidden below the row.

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 8.6, "Using an Inline Table to Display Detail Data in a Master Table".

7.5.1 How to Use the DetailStamp Facet

To use the detailStamp facet, you insert a component that is bound to the data to be displayed or hidden into the facet. 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:

  1. Drag the attribute to be displayed in the facet from the Data Control Palette onto the detailStamp facet folder. Figure 7-5 shows how the detailStamp facet folder appears in the Structure window.

    Figure 7-5 The detailStamp Facet Folder in the Structure Window

    the detailStamp facet is one of many facets for a table.
  2. From the ensuing context menu, choose the UI component to display the attribute.

  3. 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.

  4. 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>
    

7.5.2 What Happens When You Use the DetailStamp 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 as shown in Figure 7-4. Since the table was created using the findServiceRequest(Integer, String) method, you can drag the problemDescription attribute and drop it inside the detailStamp facet folder in the Structure window.

Example 7-3 shows the code JDeveloper then adds to the JSF page.

Example 7-3 JSF Code for a detailStamp Facet

<f:facet name="detailStamp">
  <af:outputText value="#{bindings.problemDescription.inputValue}"
                id="outputText7"/>
</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 7-4 shows how the code should appear after using the row variable.

Example 7-4 Modified JSF Code for a detailStamp Facet

<f:facet name="detailStamp">
  <af:outputText value="#{row.problemDescription}"
                 id="outputText7"/>
</f:facet>

7.5.3 What Happens at Runtime

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.

7.6 Enabling Row Selection in a Table

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 7-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.

Figure 7-6 The SRList Table Uses the TableSelectOne Component

Users can select one row in the SRList table.

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 7-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.

Figure 7-7 The Service History Table Uses the TableSelectMany Component

Users can select mulitple rows in the history table.

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 12.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 4.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.

Instead of using the selection facet components to set the current object and then providing a commandButton to navigate to the next page, you can use a commandLink component that allows the user to click a link to both perform an operation on a selection and navigate to another page, which saves the user the step of having to first select a row and then click the command button to perform the action and navigate. However, you must then manually set the current object on the iterator binding. For more information about manually setting the current object, see Section 7.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 set the parameter that represents the selected row for the subsequent page manually. For example, in the SRDemo application, the form on the SREdit page is created using the findServiceRequestById(Integer) method. An Integer that represents the ID for the selected row must be passed to that method in order for the form to properly display. If the parameter is not set, the form displays the first row in the iterator. For more information, see Section 10.4, "Setting Parameter Values Using a Command Component".

7.6.1 How to Use the TableSelectOne Component in the Selection Facet

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 a tableSelectMany 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 7.6.4, "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 13.6, "Adding ADF Bindings to Actions".

You can also manually add a tableSelectOne component to a selection facet.

To manually use the selection facet:

  1. In the Structure window, select af:table and choose Edit Columns from the context menu.

  2. 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).

  3. In the Structure window, expand the table's selection facet folder and select af:tableSelectOne.

  4. 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.

  5. (Optional): Rebind the Submit command button to a method or operation of your choice from the Data Control Palette. For rebinding procedures, see Section 13.6, "Adding ADF Bindings to Actions". For more information about using methods to create command buttons, see Section 10.3, "Creating Command Components to Execute Methods".

    Note:

    Until you add a command component to the facet, the value for the text attribute will not display.

7.6.2 What Happens When You Use the TableSelectOne Component

As Example 7-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 7-5 Selection Facet Code

<f:facet name="selection">
  <af:tableSelectOne text="Select and">
    <af:commandButton text="Submit"/>
</af:tableSelectOne>

As Example 7-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 component to set the selected row as the current object on the iterator.

Example 7-6 Table Selection Attributes

<af:table rows="#{bindings.findServiceRequests1.rangeSize}"
          first="#{bindings.findServiceRequests1.rangeStart}"
          var="row"  
    selectionState="#{bindings.findServiceRequests1.collectionModel.selectedRow}"
  selectionListener="#{bindings.findServiceRequests1.collectionModel.makeCurrent}"
          id="table2">

7.6.3 What Happens at Runtime

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 10.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.

7.6.4 How to Use the TableSelectMany Component in the Selection Facet

When you add the tableSelectMany component to a table that uses an ADF table binding, you must also add code that sets each selected row in turn to the current object so that the operation can be performed against that object.

To use the tableSelectMany component in an ADF application:

  1. Create the table as shown in Section 7.2.1, "How to Create a Basic Table" but do not select Enable selection.

  2. In the Structure window, expand the Table facets folder, right-click the selection facet folder, and choose Insert inside selection > TableSelectMany.

  3. 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.

  4. From the Data Control Palette, drag the method that will operate on the selected object on top of the af:tableSelectMany node. From the ensuing context menu, choose Methods > Command Button. Doing so drops the method as a command button. You now need to set the parameter value (if needed) for the method. For those procedures, see Section 10.3.1, "How to Create a Command Component Bound to a Service Method".

    For example, if you were working in the SRDemo application and wanted the user to be able to delete the selected rows, you would drag the removeEntity(Object) method onto the af:tableSelectMany node.

    You must now add logic to the method that allows the method to operate against a set of rows, making each row current in turn. To add the logic, you need to override the declarative method created when dropping the command button. For those procedures, see Section 10.5, "Overriding Declarative Methods".

    This code allows you to override the removeEntity(Object) method and add the needed logic.

  5. Add logic to the declarative method that does the following:

    • Accesses the table component

    • Obtains a list of all selected rows

    • Gets the objects in turn and performs the original method on each. To do this, the logic must loop through the list of selected rows as follows:

      • Get a row in the loop

      • Get the key for the row

      • Set it as the current object in the ADF binding

      • Delete the object by calling the declarative method

    Once that is done, logic should be added that refreshes the iterator, so that it displays the correct set of objects. For a code example, see Example 7-10.

7.6.5 What Happens When You Use the TableSelectMany Component

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

You then need to override the method and add logic that accesses each selected row in the table and executes the method on that current row.

For example, say you create a table that shows all products using the findAllProduct() method. You then add a tableSelectMany component so that a user can select the products to delete using the removeEntity(Object) method. Example 7-7 shows the code on the JSF page.

Example 7-7 JSF Code for a Table That Uses the tableSelectManyComponent

<af:table value="#{bindings.findAllProducts1.collectionModel}"
                   var="row" rows="#{bindings.findAllProducts1.rangeSize}"
                   first="#{bindings.findAllProducts1.rangeStart}"
                   id="table1">
  <af:column>
     ...
  </af:column>
  <f:facet name="selection">
    <af:tableSelectMany text="Select items and ..."
                        id="tableSelectMany1">
      <af:commandButton text="removeEntity"
                        disabled="#{!bindings.removeEntity.enabled}"
                        id="commandButton1"
                        action="#{backing_MultiDelete.commandButton1_action}"/>
    </af:tableSelectMany>
  </f:facet>
</af:table>

JDeveloper adds code to the page definition that binds the parameter value for the object in the removeEntity(Object) method to the current row of the table, as shown in Example 7-8.

Example 7-8 Method Action Binding for a Method whose Parameter is the Current Row in a Table Binding

<methodAction id="removeEntity" InstanceName="SRPublicFacade.dataProvider"
                  DataControl="SRPublicFacade" MethodName="removeEntity"
                  RequiresUpdateModel="true" Action="999">
  <NamedData NDName="entity"
                 NDValue="${bindings.findAllProducts1.currentRow.dataProvider}"
               NDType="java.lang.Object"/>
</methodAction>
<table id="findAllProducts1" IterBinding="findAllProductsIter">
  <AttrNames>
      ...
  </AttrNames>
</table>

To add logic to a declarative method, you double-click the button in the visual editor, and JDeveloper adds code to the associated backing bean that can access the method logic.

For example, if you drop the removeEntity(Object) method from the SRDemo application into the facet, and then double-click the removeEntity button in the visual editor, JDeveloper adds the code shown in Example 7-9 to the associated backing bean.

Example 7-9 Backing Bean Code for a Declarative Method

public String commandButton1_action() {
  BindingContainer bindings = getBindings();
  OperationBinding operationBinding =
     bindings.getOperationBinding("removeEntity");
  Object result = operationBinding.execute();
  if (!operationBinding.getErrors().isEmpty())
    return null;
  return null;
}

You then add code that accesses each selected row before the generated code. You use the generated code to execute the method on the object for that row. You then add code after the generated code to reexecute the query and refresh the page.

For example, say you want to allow users to delete rows of products by selecting the products and then deleting them using a command button bound to the removeEntity(Object) method. You would add the declarative code to a backing bean by double-clicking the button. You would then add code shown in bold in Example 7-10 to delete the objects. Code not in bold font is the code generated by JDeveloper, as shown in Example 7-9.

Example 7-10 Complete Backing Bean Code to Allow tableSelectMany

public String commandButton1_action() {

  //Access the tableSelectMany1 table. Note that the table name
  //is taken from the id of the table in the JSF page.
  CoreTable table = this.getTable1();
 
  //Obtain a list of all selected rows from the table
  Set rowSet = table.getSelectionState().getKeySet();
  Iterator rowSetIter = rowSet.iterator();
  
  //Use the declarative method to get the ADF bindings
  BindingContainer bindings = getBindings();
  
  //Get the object to delete. To do this, you must get the
  //iterator binding for the Products in the page definition file,
  //and cast it to DCIteratorBinding for further processing
  DCIteratorBinding pr_dcib = (DCIteratorBinding) 
     bindings.get ("findAllProductsIter");
  
  //Loop through the set of selected row numbers and delete the 
  //equivalent object from the Products collection.
  while (rowSetIter.hasNext()){
    //get the table row
    Key key = (Key) rowSetIter.next();
     
    //set the current row in the ADF binding to the same row
    pr_dcib.setCurrentRowWithKey(key.toStringFormat(true));
    
    //Obtain the Products object to delete
    RowImpl prRow = (RowImpl) pr_dcib.getCurrentRow();
   
   //Delete the object by first accessing the data and then 
   //using the generated code to execute the declarative method
   Products prObjectToDelete = (Products) prRow.getDataProvider();
   OperationBinding operationBinding =
     bindings.getOperationBinding("removeEntity");
     
  //You don't need to set the parameter, as this was done
  //declaritively when you dropped the button on the page
  Object result = operationBinding.execute();
  if (!operationBinding.getErrors().isEmpty())
     return null;
  }     
  
//Re-execute the query to refresh the screen
OperationBinding requery = bindings.getOperationBinding("findAllProducts");
requery.execute();

//Stay on the same page, so no returned outcome needed
return ];
        
}

7.6.6 What Happens at Runtime

When the user selects multiple rows and then clicks the command button, the application accesses the table to determine each of the selected rows, and creates a rowset for those rows. The application then accesses the binding container, and from that container, accesses the iterator used to manage the complete collection and casts it to a generic iterator binding that can manage the rowset of selected rows.

That iterator then goes through each row, and for each row:

  • Sets a key

  • Uses that key to set the row to the current row in the iterator, using the setCurrentRowWithKey operation, as described in Table 6-1, "Built-in Navigation Operations"

  • Uses the current row to create the object against which the method will be executed

  • Accesses the associated data for the object

  • Executes the method

Once that is complete, and there are no more rows in the rowset, the application accesses the iterator in the binding container and reexecutes the query to refresh the set of rows displayed in the table.

7.7 Setting the Current Object Using a Command Component

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 7-8, which the user can click to directly edit a service request, without needing to first select the row.

Figure 7-8 Command Links Used in a Table on the SRList Page

Command links for the ID set the curent 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.

7.7.1 How to 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 .

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.

To set the current row:

  1. From the Data Control Palette, drag the setCurrentRowWithKey or setCurrentRowWithKeyValue operation.

  2. From the context menu, choose Operations > ADF Command Link.

  3. In the Action Binding Editor, you need to set the value for the rowKey parameter. By default, it 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 7-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 7.2.2.2, "Code on the JSF Page for an ADF Faces Table".

7.7.2 What Happens When You Set the Current Row

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 10.3, "Creating Command Components to Execute Methods").

Example 7-11 shows the code on the page definition file created when you drop the setCurrentRowWithKey operation and set #{row.svrId} as the value for the rowKey parameter.

Example 7-11 Page Definition Code for the setCurrentRowWithKey Operation

<action id="setCurrentRowWithKey" IterBinding="findServiceRequestsIter"
            InstanceName="SRPublicFacade.dataProvider"
            DataControl="SRPublicFacade" RequiresUpdateModel="false"
            Action="96">
  <NamedData NDName="rowKey" NDValue="${row.rowKeyStr}"
             NDType="java.lang.String"/>
</action>

7.7.3 What Happens At Runtime

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 10.4, "Setting Parameter Values Using a Command Component".