24 Feb 2013

Dynamic Tree Binding Definition

ADF table component usually requires a tree binding definition in the PageDef file pointing to some iterator binding.  For example a table rendering rows of some VEmp view object would require the following structure in the PageDef file:

  <executables>
    <iterator Binds="VEmp" RangeSize="25" DataControl="AppModuleDataControl"        id="VEmpIterator"/>
  </executables>
  <bindings>
    <tree IterBinding="VEmpIterator" id="VEmp">
      <nodeDefinition DefName="com.cs.blog.dyntreebindingdemo.model.VEmp"
                      Name="VEmp0">
        <AttrNames>
          <Item Value="EmployeeId"/>
          <Item Value="FirstName"/>
          <Item Value="LastName"/>
          <Item Value="Email"/>
          <Item Value="PhoneNumber"/>
          <Item Value="HireDate"/>
          <Item Value="JobId"/>
          <Item Value="Salary"/>
          <Item Value="CommissionPct"/>
          <Item Value="ManagerId"/>
          <Item Value="DepartmentId"/>
          <Item Value="CreatedBy"/>
          <Item Value="CreatedDate"/>
          <Item Value="ModifiedBy"/>
          <Item Value="ModifiedDate"/>
          <Item Value="ActionComment"/>
          <Item Value="CreatedBy1"/>
        </AttrNames>
      </nodeDefinition>
    </tree>
  </bindings>

In this post I'm going to show how we can create a tree binding definition and a corresponding iterator binding dynamically at runtime.

Let's consider a managed bean method returning a tree binding definition:

private static final String VO_NAME = "VEmp";
private static final String ITERATOR_NAME = "VEmpIterator";
private static final String TREE_NAME = "VEmp";   
private static final String DATACONTROL_NAME = "AppModuleDataControl";   

public JUCtrlHierBinding getTree() {
    DCBindingContainer dcb = getBindings();

    //May be the VEmp tree binding is already created
    JUCtrlHierBinding chb = (JUCtrlHierBinding)dcb.findCtrlBinding(TREE_NAME);

    if (chb == null) { // if not

        //Looking for the VEmpIterator iterator
        JUIteratorBinding iter =
            (JUIteratorBinding)getIterator(ITERATOR_NAME, VO_NAME);

        //Create and init a tree binding definition
        JUCtrlHierDef hierDef = new FacesCtrlHierDef();
        HashMap initValues = new HashMap();
        initValues.put(JUCtrlHierDef.PNAME_IterBinding, iter.getName());
        JUCtrlHierTypeBinding typeBinding = new JUCtrlHierTypeBinding();
        initValues.put(JUCtrlHierDef.PNAME_TypeBindings,
                       new JUCtrlHierTypeBinding[] { typeBinding });
        hierDef.init(initValues);

        //Create a tree binding instance
        chb = (JUCtrlHierBinding)hierDef.createControlBinding(dcb);

        //Add the instance to the current binding container
        dcb.addControlBinding(TREE_NAME, chb);
    }
    return chb;
}

And getIterator method looks for the corresponding iterator binding and creates it if it doesn't exist yet:

public DCIteratorBinding getIterator(String iteratorName, String voName)
{
  DCBindingContainer dcb = getBindings();

  //It could be created earlier 
  DCIteratorBinding jub = dcb.findIteratorBinding(iteratorName);

  if (jub==null) {// if not
    //Create and init an iterator binding definition
    JUIteratorDef jid = new JUIteratorDef(iteratorName, null, voName, null, 25);
    HashMap initValues = new HashMap();
    initValues.put(JUTags.DataControl , DATACONTROL_NAME);
    jid.init(initValues);

    //Create an iterator binding instance
    jub = jid.createIterBinding(BindingContext.getCurrent(), dcb);        

    //Add the instance to the current binding container
    dcb.addIteratorBinding(iteratorName, jub);
  }
  return jub;
}
The readonly table using our dynamic tree binding should look like this:

<af:table rows="#{DynTableBean.tree.rangeSize}"
          fetchSize="#{DynTableBean.tree.rangeSize}"
          emptyText="#{DynTableBean.tree.viewable ? 'No data to display.' : 'Access Denied.'}"
          var="row" rowBandingInterval="0"
          value="#{DynTableBean.tree.collectionModel}"
          selectedRowKeys="#{DynTableBean.tree.collectionModel.selectedRow}"
          selectionListener="#{DynTableBean.tree.collectionModel.makeCurrent}"
          rowSelection="single" id="dc_t1">
  <af:forEach items="#{DynTableBean.tree.attributeDefs}" var="def">
    <af:column headerText="#{DynTableBean.tree.labels[def.name]}" sortable="true"
               sortProperty="#{def.name}" id="dc_c1">
      <af:outputText value="#{row[def.name]}" id="dc_ot1"/>
    </af:column>
  </af:forEach>
</af:table>

The technique shown in this post can be easily used to build a declarative component responsible for browsing data of any view object passed to the component as an attribute.

That's it!

3 comments:

  1. Very Useful article. Thank you. I've been struggled out with this issue.
    As well as this, do you know for Updatable table usage which has LOV?
    How to define a list binding and attach it to some column?

    ReplyDelete
    Replies
    1. You can find an example of a dynamic LOV here:
      http://adfpractice-fedor.blogspot.com/2012/10/deep-dive-into-oracle-adf-advanced.html

      Delete
  2. Another way:
    http://stefanoba.blogspot.it/2013/11/attribute-binding-dynamically-created.html

    ReplyDelete

Post Comment