Skip Headers
Oracle® Application Development Framework Developer's Guide
10g Release 3 (10.1.3)
B25386-01
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

7.6 Enable 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 left most column that allows a user to select one row, or one or more rows, and then take some action on those rows.

The tableSelectOne component allows the user to select just one row. This component provides a radio button for each row. For example, Figure 7-6 shows the table in the SRList page. The tableSelectOne component allows a user to select a row and then either view or edit the details for the associated 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 checkboxes from which the user can select one or more rows. When you use the tableSelectMany tag, links are added that allow the user to select all or none of the rows. Figure 7-7 shows the table on the SRMain page of the SRDemo application. The tableSelectMany component allows a user to select multiple records to delete.

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

Users can select mulitple rows in the history table.

Both components have a text attribute whose value can be instructions for the user. These components 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, "Adding Validation".

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 is ultimately determined by the iterator. The tableSelectOne component is able to show the current data object determined by the iterator 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.

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, "Passing Parameter Values to Another Page 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 included in the selection facet, along with an Update command button.


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

Since you may wish to have the button bound to a method, you need to rebind the Update button to the method of your choice. For rebinding procedures, see Section 13.5, "Adding ADF Bindings to Actions".

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

To manually use the selection facet:

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

  2. In the Edit Columns dialog, select Enable Selection and click OK.

    JDeveloper adds the tableSelectOne component (plus the needed listeners and attributes that work with the selection), to the selection facet folder.

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

  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 Update button to a method of your choice from the Data Control Palette. For rebinding procedures, see Section 13.5, "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 use the selection facet when you first create 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.

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 or tableSelectMany 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 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 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 add selection capabilities.

  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 table node and in the Property Inspector, delete the values for the SelectionState and SelectionListener. 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 will create.

  4. From the Data Control Palette, drag the method that will operate on the selected object on top of the tableSelectMany node.

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

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

    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.

  6. 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 method bound to a method, JDeveloper does the following:

  • Adds the tableSelectMany and command components to the selection facet

  • Creates a method binding for the bound method, including a NamedData element used 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 managed 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.