X

Technical Articles relating to Oracle Development Tools and Frameworks

  • ADF
    October 9, 2014

Showing Initial Selection in Your ListView

Duncan Mills
Architect

If you use a ListView or a Table component which enables selection by the user in a master detail view you may feel that there is a slight inconsistency of behaviour. When the user first enters the screen it may, by default, look something like this (in this example a ListView to the left and the "selected" department detail to the right in a form layout):

ListView showing no initial selection

Notice that the associated detail form is showing the first row in the collection as you would expect, but visually, the corresponding row in the ListView is not visually highlighted. Compare that with what happens when the user clicks on a row in the ListView to select another row:

ListView with a selection made by the user

Now the selected row in the listView is highlighted with the skin's selection colour.

So the question is, how can we show the page initially in a manner consistent with this?
e.g.

Corrected initial selection

Well in fact the binding layer already has everything you need and it's a trivial thing to fix. All we have to do here is to set the selectedRowKeys attribute of the listView tag. So, assuming we have a listView showing departments the tag would look something like this:

<af:listView value="#{bindings.DepartmentsView1.collectionModel}" 
             var="item"
             selectedRowKeys="#{bindings.DepartmentsView1.collectionModel.selectedRow}" 
             selection="single" 
             selectionListener="#{bindings.DepartmentsView1.collectionModel.makeCurrent}"
             emptyText="#{bindings.DepartmentsView1.viewable ? 'No data to display.' : 'Access Denied.'}"
             fetchSize="#{bindings.DepartmentsView1.rangeSize}" 
             id="lv1">

Doing It From Scratch

So this is an example where the binding layer does everything for you, great! However, what wanted to do this manually? I'm showing you this because it happens to illustrate a few useful techniques and code snippets.  In general I'd stick with the simple approach above though! 

Setting this up requires some simple steps and a small amount of code, so let's go.

Step 1: Defining the Selected Row Keys

The core mechanism of this solution is to pre-seed the selectedRowKeys property of the listView component with the row key of whichever row is current in the underlying collection.  So the first step is to define somewhere to hold that row key set. So we start by defining a ViewScope managed bean (called lvState in this example) and within that, we just define a variable to hold a RowKeySet reference.

So we start with the Java class:

package oracle.adf.demo;
import org.apache.myfaces.trinidad.model.RowKeySet;
public class ListViewState {
RowKeySet _selectedRowKeys;
public void setSelectedRowKeys(RowKeySet rks)
{
this._selectedRowKeys; = rks;
}
public RowKeySet getSelectedRowKeys() 
{
return _selectedRowKeys;
}
}

And define that as a managed bean in the relevant task flow definition:

<managed-bean>
   <managed-bean-name>lvState</managed-bean-name>
   <managed-bean-class>oracle.adf.demo.ListViewState</managed-bean-class>
   <managed-bean-scope>view</managed-bean-scope>
</managed-bean>

Once that's defined you can update your <af:listView> tag to reference it:

<af:listView value="#{bindings.DepartmentsView1.collectionModel}" 
             var="item"
             selectedRowKeys="#{viewScope.lvState.selectedRowKeys}" 
             selection="single" 
             selectionListener="#{bindings.DepartmentsView1.collectionModel.makeCurrent}"
             emptyText="#{bindings.DepartmentsView1.viewable ? 'No data to display.' : 'Access Denied.'}"
             fetchSize="#{bindings.DepartmentsView1.rangeSize}"
             id="lv1"> 

Step 2: Pre-Seeding the Row Key Set 

As defined, the row key set picked up by the listView will of course be null until the user physically makes a selection - we're no better off than before. So we now have to work out a way of taking the current selection from the model / binding layer, obtaining it's key and then setting up the row key set before the listView is rendered.

The simplest way of doing this is to utilise the capability of JSF 2 to simply define events to be executed as the page is being set up for rendering. In this case we use the postAddToView event inside of the listView to execute the setup code:

<af:listView ...>
  <f:event type="postAddToView" listener="#{lvEvents.setupListViewSelection}"/>
  <af:listItem ...>

The event tag points at a request scope managed bean called lvEvents.  This bean needs access to the lvState managed bean that we defined in the first step:

<managed-bean>
  <managed-bean-name>lvEvents</managed-bean-name>
  <managed-bean-class>oracle.adf.demo.ListViewEventManager</managed-bean-class>
  <managed-bean-scope>view</managed-bean-scope>
  <managed-property>
    <property-name>state</property-name>
    <property-class>oracle.adf.demo.ListViewState</property-class>
    <value>#{lvState}</value>
  </managed-property>
</managed-bean>

The ListViewEventManager class should therefore have a variable to hold the state with the appropriate getter and setter:

public class ListViewEventManager {
  ListViewState _state;
    public void setState(ListViewState _state) {
        this._state = _state;
    }
    public ListViewState getState() {
        return _state;
    }
    ...

Finally, the implementation of the listener defined in the <f:event> tag needs to be added to the class:

public void setupListViewSelection(ComponentSystemEvent componentSystemEvent) {
  if (getState().getRks() == null) {
     BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
     DCIteratorBinding iterBind =
                ((DCBindingContainer)bindings).findIteratorBinding("DepartmentsView1Iterator");
            Key key = iterBind.getCurrentRow().getKey();
            RowKeySet rks = new RowKeySetImpl();
            rks.add(Collections.singletonList(key));
            getState().setRks(rks);
        }
}    

So now, when the page renders, if the selection row key set has not been setup due to user interaction we will go ahead and create it, seeding the rowkey of whatever row the model considers to be current.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha