ADF af:table restoring table state

Max picture Max · Nov 20, 2012 · Viewed 7.2k times · Source

I have a problem regarding ADF Faces tables and dynamic regions.

Preconditions:
Oracle JDeveloper 11.1.1.6.0

I am using ADF Faces and ADF Binding layer to display a list of Java POJO in an af:table. The ADF table is displayed on a view inside a bounded task flow.
This task flow is used in a dynamic region on another view.
Navigation contains two links which are used to choose via bean-property which bounded task flow to display in the dynamic region.
The dynamic region receives an „Input Parameter Map“ via task flow binding.
This map contains the POJO to display in the table and a state-Bean which should hold table state and state of possible other components located on the view (see implementation below).

Problem/ requirements:

1. Preselection:

Preselect a single row in the table according to data contained in the POJO on first load of the table.

2. Keep selection over taskflow switch:

The selected row in the table (bounded taskflow A) should also be selected after switching to another bounded taskflow B and back to A (where the table is located).

3. No MDS

No usage of savepoints/ MDS features.

4. Consistent table model and binding layer

It should be possible to ask the table component and the binding for the selected row after switching between the task flows and after selecting elements in the table.
The table row and the selected row in binding should be consistent after switching the task flows and after selecting elements in the table.

Already tried:

Preselection of a row and keep selection over task flow switch without use of MDS/ savepoints.

I have bound some table properties to a “state” bean. Implementation of the bean is attached below.

1. Properties of the table:

Selected row keys:
selectedRowKeys="#{pageFlowScope.stateBean.tableStateBean.rowKeySet}"

Backing-Bean property to bind RichTable:
binding="#{pageFlowScope.stateBean.tableStateBean.richTable}"

Default selection Listener
selectionListener="#{bindings.postanschriften.collectionModel.makeCurrent}"

What we tried here:
On initial load of the table and changing the row bean method getRowKeySet(..) is called.
In this method the row to select is calculated on initial table load (this.rowKeySet == null). Because table property selectedRowKeys is bound to pageFlowScope.stateBean.tableStateBean.rowKeySet the property in the bean is updated if another row is selected in table. If we change the task flow from A to B and vice versa the getter of backing bean property richTable is called. In the getter method the selection state is restored.

2. Implementation of the “state” bean:

public class TableStateBean {
    private RichTable richTable;
    private RowKeySet rowKeySet;
    private String bindingIteratorName;
    private RowMatcher matcher;

public RowKeySet getRowKeySet() {
    if (this.rowKeySet == null) {
        preselectRow();
    }
    return rowKeySet;
}

public RichTable getRichTable() {
    if (richTable != null && rowKeySet != null) { 
        RowKeySet currentTableSelection = getCurrentTableSelection();
        RowKeySet tableSelectionToRestore = getTableSelectionToRestore();
        executeSelection(tableSelectionToRestore, currentTableSelection);
    }
    return richTable;
}

public void setRichTable(RichTable pRichTable) {
    richTable = pRichTable;
}

public void doTableSelection(){
    preselectRow();
}


private RowKeySet getCurrentTableSelection() {
    Row currentRow = (Row) JSFUtils.resolveExpression("#{bindings." + this.bindingIteratorName + ".currentRow}");
    Key currKey = currentRow.getKey();
    ArrayList<Key> lst = new ArrayList<Key>(1);
    lst.add(currKey);
    RowKeySet keySet = new RowKeySetImpl();
    keySet.add(lst);
    return keySet;
}

private RowKeySet getTableSelectionToRestore() {
    RowKeySet tableSelectionToRestoreRow = null;
    RowSetIterator rowSetIterator = extractRowSetIterator();
    int tableRowIndexOfCurrentSelectedKey = getSingleRowKeyIndexValue(this.rowKeySet);

    if (tableRowIndexOfCurrentSelectedKey != -1) {
        Row currentRow = rowSetIterator.first();
        for (int i = 0; rowSetIterator.hasNext() && i < tableRowIndexOfCurrentSelectedKey; i++) {
            currentRow = rowSetIterator.next();
        }
        if (currentRow != null) {
            Key newSelectionKey = currentRow.getKey();

            ArrayList<Key> keyList = new ArrayList<Key>(1);
            keyList.add(newSelectionKey);

            tableSelectionToRestoreRow = new RowKeySetImpl();
            tableSelectionToRestoreRow.add(keyList);
        }
    }

    return tableSelectionToRestoreRow;
}

private int getSingleRowKeyIndexValue(RowKeySet rowKeySet) {
    int tableRowIndexOfCurrentSelectedKey = -1;

    if (rowKeySet != null) {
        Object[] rowKeySetArray = rowKeySet.toArray();
        List<Key> selectedRowKeys = (rowKeySetArray.length > 0) ? (List<Key>) rowKeySetArray[0] : new ArrayList<Key>();
        if (selectedRowKeys.size() > 0) {
            Key currentSelectedKey = selectedRowKeys.get(0);
            Object[] attributeValues = currentSelectedKey.getAttributeValues();
            assert (attributeValues.length > 0);
            tableRowIndexOfCurrentSelectedKey = (Integer) attributeValues[0];
        }
    }
    return tableRowIndexOfCurrentSelectedKey;
}

private void executeSelection(RowKeySet newCurrentRow, RowKeySet oldCurrentRow) {
    SelectionEvent selectionEvent = new SelectionEvent(oldCurrentRow, newCurrentRow, richTable);
    selectionEvent.queue();
    AdfFacesContext.getCurrentInstance().addPartialTarget(richTable);
}

protected void preselectRow() {
    RowSetIterator rowSetIterator = extractRowSetIterator();

    RowKeySet oldSelection = getCurrentTableSelection();        
    Row currentRow = rowSetIterator.first();
    while (rowSetIterator.hasNext() 
           && (!matcher.match(currentRow))) // Matcher selects which row should be displayed according to the Object bound behind the binding-layer.
    {
        currentRow = rowSetIterator.next();
    }
    if (currentRow != null) {

        Key key = currentRow.getKey();
        RowKeySet newSelection = createRowKeySet(key);            
        setActiveRowKey(key);
        executeSelection(newSelection, oldSelection);
        setRowKeySet(newSelection);
    }
}

private void setActiveRowKey(Key pKey) {
    ArrayList<Key> lst = new ArrayList<Key>(1);
    lst.add(pKey);
    this.richTable.setActiveRowKey(lst);
}

private RowKeySet createRowKeySet(Key pKey) {
    ArrayList<Key> lst = new ArrayList<Key>(1);
    lst.add(pKey);
    RowKeySetImpl rowKeySetToCreate = new RowKeySetImpl();
    rowKeySetToCreate.add(lst);
    return rowKeySetToCreate;
}

private RowSetIterator extractRowSetIterator() {
    DCIteratorBinding iteratorBinding = ADFUtils.findIterator(this.bindingIteratorName);
    RowSetIterator rowSetIterator = iteratorBinding.getRowSetIterator();
    return rowSetIterator;
    }
}

It seems that in some cases the binding layer is not synchronous to the elements selected on the RichTable component. So I guess the solution mentioned above is not very robust.

Is there another way to archive the functionality mentioned above in a robust manner? I think to preselect a row in a table according to some values in the bound POJO is not so strange. And also keeping the selected row of a table after switching between bounded task flows (dynamic region) should be possible, isn´t it?

Thank you in anticipation for your help

Regards,

Max

Answer

User404 picture User404 · Nov 20, 2012

Setting a 'selected' row is rather easy. There are several solutions for that. This is just one of them : Make a binding for your table to you backing bean. In the getter you add the following code:

DCBindingContainer bindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
DCIteratorBinding dcItteratorBindings =
bindings.findIteratorBinding("yourViewObjectIterator");
RowSetIterator it = dcItteratorBindings.getRowSetIterator();
Rows[] rows = voTableData.getAllRowsInRange();
Row needsSelection = null;
for(Row r: rows)
{
   //just search for the row you want to set selected
  if(r.getAttribute("SomeAttribute").eqauls(..))
     needsSelection = r;
}

if(needsSelection != null)
it.setCurrentRow(needsSelection);