Tuesday Jun 12, 2012

Programmatically disclosing a node in af:tree and af:treeTable

A common developer requirement when working with af:tree or af:treeTable components is to programmatically disclose (expand) a specific node in the tree.

If the node to disclose is not a top level node, like a location in a LocationsView -> DepartmentsView -> EmployeesView hierarchy, you need to also disclose the node's parent node hierarchy for application users to see the fully expanded tree node structure. Working on ADF Code Corner sample #101, I wrote the following code lines that show a generic option for disclosing a tree node starting from a handle to the node to disclose.

The use case in ADF Coder Corner sample #101 is a drag and drop operation from a table component to a tree to relocate employees to a new department. The tree node that receives the drop is a department node contained in a location. In theory the location could be part of a country and so on to indicate the depth the tree may have. Based on this structure, the code below provides a generic solution to parse the current node parent nodes and its child nodes.

The drop event provided a rowKey for the tree node that received the drop. Like in af:table, the tree row key is not of type oracle.jbo.domain.Key but an implementation of java.util.List that contains the row keys. The JUCtrlHierBinding class in the ADF Binding layer that represents the ADF tree binding at runtime provides a method named findNodeByKeyPath that allows you to get a handle to the JUCtrlHierNodeBinding instance that represents a tree node in the binding layer.

CollectionModel model = (CollectionModel) your_af_tree_reference.getValue();
JUCtrlHierBinding treeBinding = (JUCtrlHierBinding ) model.getWrappedData();
JUCtrlHierNodeBinding treeDropNode = treeBinding.findNodeByKeyPath(dropRowKey);

To disclose the tree node, you need to create a RowKeySet, which you do using the RowKeySetImpl class. Because the RowKeySet replaces any existing row key set in the tree, all other nodes are automatically closed.

RowKeySetImpl rksImpl = new RowKeySetImpl();
//the first key to add is the node that received the drop
//operation (departments).            
rksImpl.add(dropRowKey);    

Similar, from the tree binding, the root node can be obtained. The root node is the end of all parent node iteration and therefore important.

JUCtrlHierNodeBinding rootNode = treeBinding.getRootNodeBinding();

The following code obtains a reference to the hierarchy of parent nodes until the root node is found.

JUCtrlHierNodeBinding dropNodeParent = treeDropNode.getParent();
//walk up the tree to expand all parent nodes 
while(dropNodeParent != null && dropNodeParent != rootNode){
   //add the node's keyPath (remember its a List) to the row key set
   rksImpl.add(dropNodeParent.getKeyPath());   
   dropNodeParent = dropNodeParent.getParent();
}

Next, you disclose the drop node immediate child nodes as otherwise all you see is the department node. Its not quite exactly "dinner for one", but the procedure is very similar to the one handling the parent node keys

ArrayList<JUCtrlHierNodeBinding> childList = (ArrayList<JUCtrlHierNodeBinding>) treeDropNode.getChildren();                     
for(JUCtrlHierNodeBinding nb : childList){
  rksImpl.add(nb.getKeyPath());
}

Next, the row key set is defined as the disclosed row keys on the tree so when you refresh (PPR) the tree, the new disclosed state shows

tree.setDisclosedRowKeys(rksImpl); 
AdfFacesContext.getCurrentInstance().addPartialTarget(tree.getParent()); 

The refresh in my use case is on the tree parent component (a layout container), which usually shows the best effect for refreshing the tree component. 

Wednesday Jan 18, 2012

How-to determine the ADF tree node type using EL

Creating an ADF tree in ADF produces an entry similar to this in the PageDef file of the view.
<tree IterBinding="AllCountriesIterator" id="AllCountries">
   <nodeDefinition 
        DefName="oracle.summit.model.views.CountryVO" Name="AllCountries0">
     <AttrNames>
       <Item Value="Country"/>
     </AttrNames>
     <Accessors>
       <Item Value="CustomerVO"/>
     </Accessors>
   </nodeDefinition>
   <nodeDefinition 
      DefName="oracle.summit.model.views.CustomerVO" Name="AllCountries1"
      TargetIterator="${bindings.AllCustomersIterator}">
      <AttrNames>
        <Item Value="Id"/>
        <Item Value="Name"/>
     </AttrNames>
   </nodeDefinition>
</tree>

Notice the DefName attribute on each node containing a reference to the actual View Object instance used to render a specific node.

With this information you can now use EL to render the tree nodes differently. For example, the page source below renders the node as a command link if the node presents a customer. For Countries, the node is simply rendered as an output text.
<af:tree value="#{bindings.AllCountries.treeModel}" var="node" ...>
  <f:facet name="nodeStamp">
    <af:group id="g1">
      <af:commandLink id="cl4" text="#{node}"                                                                             
                      rendered="#{node.hierTypeBinding.viewDefName ==  
                                       'oracle.summit.model.views.CustomerVO'}"
      .../>
      
      <af:outputText id="ot4" value="#{node}"                                                                               
               rendered="#{node.hierTypeBinding.viewDefName == 
                                     'oracle.summit.model.views.CountryVO'}"/>
    </af:group>
  </f:facet>
</af:tree>       
The EL expression  #{node.hierTypeBinding.viewDefName} returns the name of the node type, which in ADF is the absolute name of the collection instance rendering the node.

Tuesday Nov 22, 2011

How-to read data from selected tree node

By default, the SelectionListener property of an ADF bound tree points to the makeCurrent method of the FacesCtrlHierBinding class in ADF to synchronize the current row in the ADF binding layer with the selected tree node. To customize the selection behavior, or just to read the selected node value in Java, you override the default configuration with an EL string pointing to a managed bean method property. In the following I show how you change the selection listener while preserving the default ADF selection behavior.

To change the SelectionListener, select the tree component in the Structure Window and open the Oracle JDeveloper Property Inspector. From the context menu, select the Edit option to create a new listener method in a new or an existing managed bean.

For this example, I created a new managed bean. On tree node select, the managed bean code prints the selected tree node value(s)


import java.util.List;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.MethodExpression;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;

import java.util.Iterator;  
import oracle.adf.view.rich.component.rich.data.RichTree;
import oracle.jbo.Row;
import oracle.jbo.uicli.binding.JUCtrlHierBinding;
import oracle.jbo.uicli.binding.JUCtrlHierNodeBinding;
import org.apache.myfaces.trinidad.event.SelectionEvent;

import org.apache.myfaces.trinidad.model.CollectionModel;
import org.apache.myfaces.trinidad.model.RowKeySet;
import org.apache.myfaces.trinidad.model.TreeModel;  
public class TreeSampleBean {
 public TreeSampleBean() {}
 public void onTreeSelect(SelectionEvent selectionEvent) {
  //original selection listener set by ADF
  //#{bindings.allDepartments.treeModel.makeCurrent}
  String adfSelectionListener = "#{bindings.allDepartments.treeModel.makeCurrent}";
  //make sure the default selection listener functionality is 
  //preserved. you don't need to do this for multi select trees 
  //as the ADF binding only supports single current row selection  
 
  /* START PRESERVER DEFAULT ADF SELECT BEHAVIOR */
  FacesContext fctx = FacesContext.getCurrentInstance();
  Application application = fctx.getApplication();
  ELContext elCtx = fctx.getELContext();
  ExpressionFactory exprFactory = application.getExpressionFactory();
  
  MethodExpression me = null;
  me = exprFactory.createMethodExpression(elCtx, adfSelectionListener, 
                                          Object.class, newClass[]{SelectionEvent.class}); 
  me.invoke(elCtx, new Object[] { selectionEvent });  

  /* END PRESERVER DEFAULT ADF SELECT BEHAVIOR */
  RichTree tree = (RichTree)selectionEvent.getSource();
  TreeModel model = (TreeModel)tree.getValue();   
 
  //get selected nodes
  RowKeySet rowKeySet = selectionEvent.getAddedSet(); 
  Iterator rksIterator = rowKeySet.iterator(); 
  //for single select configurations,this only is called once 
  while (rksIterator.hasNext()) {
    List key = (List)rksIterator.next();
    JUCtrlHierBinding treeBinding = null;
    CollectionModel collectionModel = (CollectionModel)tree.getValue();
    treeBinding = (JUCtrlHierBinding)collectionModel.getWrappedData(); 
    JUCtrlHierNodeBinding nodeBinding = null;
    nodeBinding = treeBinding.findNodeByKeyPath(key);
    Row rw = nodeBinding.getRow();
    //print first row attribute. Note that in a tree you have to 
    //determine the node type if you want to select node attributes 
    //by name and not index 
    String rowType = rw.getStructureDef().getDefName();
  
    if(rowType.equalsIgnoreCase("DepartmentsView")){
      System.out.println("This row is a department: " +  
                         rw.getAttribute("DepartmentId"));
    }
    else if(rowType.equalsIgnoreCase("EmployeesView")){
     System.out.println("This row is an employee: " + 
                         rw.getAttribute("EmployeeId"));
    }     
    else{
      System.out.println("Huh????");  
    }
    // ... do more useful stuff here   
  }   

}

--------------------

Download JDeveloper 11.1.2.1 Sample Workspace


About

The Oracle JDeveloper forum ranks in the Top 5 of the most active forums on the Oracle Technology Network (OTN).



The OTN Harvest blog is a summary of selected topics posted on the OTN Oracle JDeveloper forum.



It is an effort to turn knowledge exchange into an interesting read for developers who enjoy little nuggets of wisdom





Frank Nimphius

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today