ADF Faces: ADF Faces Tree Drilldown Example   Jan 3, 2009

copied completely from the following site, author unknown to me

http://www.mediafly.com/Podcasts/Episodes/ADF_Faces_ADF_Faces_Tree_Drilldown_Example

 

Another lost example: The workspace linked by this blog entry shows how to use a command link to drill down to detail records shown in a table.

The drilldown functionality - though not shown as a stand alone example - is documented by Steve Muench in the context of the ADF BC version in the SRDemo and the ADF Business Components developer guide. In this blog entry I'll give you two examples - the one provided in the developer guide and one less declarative but code centric example.

The ADF BC Developer Guide version

The ADF BC Developer Guide version sets the row currency for the data displayed in the form by clicking on the tree node links. To get to a specific user entry, you click the location node, expand the department node and then click onto the user name entry.

Clicking the department node entry shows the first record by default, which allows you to build an input form with navigation buttons. While this works nicely, having to click on each and every tree node along the path down to the user entry may not be considered user friendly.

Firstly, make sure you created iterators for each of the ViewObjects invloved: LocationsView, DepartmentsView, EmployeesView

This solution is implemented through the setCurrentRowWithKey operation, which is exposed as an operation on the LocationView, DepartmentsView and EmployeesView. Open the pageDef file in the Structure Window and select the bindings node.

From the context menu select the option to create a new Action. Select the LocationView instance that represents the top view in the tree structure. Choose the setCurrentRowWithKey option from the drop down.

Its important to set the rowKey value to #{node.rowKeyStr} so that the selected node's key value is passed to the operation. the "node" string is a variable name contained in the page when building the tree and you have to type it into the value field (the ExpressionLanguage builder doesn't see this variable name)

After building the action, make sure you give it a good and readable name by changing the default name in the property inspector for the action.

Next step is to create a binding of the tree component to a managed bean. The binding is used later to detect the depth of the tree, which is important for the command link to access the right setCurrentRowsWithKey action.

The tree nodes are defined by three command link components, according to the expected tree depth of location -- department -- employees.

Each of the command links is displayed or hidden based on one of the following conditions added to the "rendered" property

#{TreeManagedBean.treeHandler.depth==0}

#{TreeManagedBean.treeHandler.depth==1}

#{TreeManagedBean.treeHandler.depth==2}

This means that e.g. the command link with the ActionListener set to execute the setCurrentRowWithKey operation for the employees view, is shown or hidden by #{TreeManagedBean.treeHandler.depth==2}

Last, but not least, each of the command links needs to be linked to one of the setCurrentRowWithKey actions.

This way, when the node is clicked on, the underlying ViewObject row currency is set to the selected row. Also note that the command links don't use PPR (partial submit = false).

PLAIN TEXT

XML:
      id="nodeAction1"
      actionListener="#{bindings.setCurrentLocationRowWithKey.execute}"
      rendered="#{TreeManagedBean.treeHandler.depth==0}"/>
 
      id="nodeAction2"
      rendered="#{TreeManagedBean.treeHandler.depth==1}"
      actionListener="#{bindings.setCurrentDepartmentsRowWithKey.execute}"/>


      id="nodeAction3"
      rendered="#{TreeManagedBean.treeHandler.depth==2}"
      actionListener="#{bindings.setCurrentEmployeesRowWithKey.execute}"/>



Note that for the JSF form to show the selected employee, the input form must be based on the EmployeeView instance contained in the LocationView tree.


Custom example

The custom example solves the usability concern of the version documented in the ADF BC developer guide so tha only the leaf nodes are shown as hyperlinks. Later, in the implementation, you will see that the risk with this version lies in how the node tree key is parsed.

As for the documented ADF BC tree drill down example, you need to create iterator bindings for all the ViewObject instances that are involved. Also you need to create a binding from the tree component to a managed bean.

Instead of using three command links that are used in the previouse example, you use one output text component and one command link. The command link is set to partial submit and also has an id assigned to it so it triggers the refresh of the JSF input form.

Note that the output text component is rendered for all tree depth that are below 2, whcih means for locations and employees.

The ActionListener property on the commandLink component is set to #{TreeManagedBean.nodeSelected}, which is a managed bean method.

PLAIN TEXT

CODE:


public void nodeSelected(ActionEvent actionEvent){
      String rowKeyStr = getTreeHandler().getRowKey().toString();
      String[] path = rowKeyStr.split(", ");
      if (getTreeHandler().getDepth()==2){
      int locIndx = new Integer(path[0].substring(1)).intValue();
      int depIndx = new Integer(path[1].substring(0)).intValue();
      int empIndx = new Integer(path[2].substring(0,path[2].indexOf("]"))).intValue();
      // System.out.println(locIndx);
      // System.out.println(depIndx);
      // System.out.println(empIndx);
      FacesContext fctx = FacesContext.getCurrentInstance();
      ValueBinding vb = fctx.getApplication().createValueBinding("#{bindings.LocationsView1Iterator}");
      DCIteratorBinding dciterLocationsView = (DCIteratorBinding)vb.getValue(fctx);
     dciterLocationsView.getRowSetIterator().setCurrentRow(dciterLocationsView.getRowAtRangeIndex(locIndx));
     vb = fctx.getApplication().createValueBinding("#{bindings.DepartmentsView3Iterator}");
  DCIteratorBinding dciterDepartmentsView = (DCIteratorBinding)vb.getValue(fctx);
      dciterDepartmentsView.getRowSetIterator().setCurrentRow(dciterDepartmentsView.getRowAtRangeIndex(depIndx));
     vb = fctx.getApplication().createValueBinding("#{bindings.EmployeesView4Iterator}");
      DCIteratorBinding dciterEmployeesView = (DCIteratorBinding)vb.getValue(fctx);
     int empRangeSize = dciterEmployeesView.getRangeSize();
     int empRangeIndx = 0;
     int empRowIndx = 0;

      if (empIndx + 1> empRangeSize){
      empRangeIndx = empIndx/ empRangeSize;
      empRowIndx = empIndx % empRangeSize;
      dciterEmployeesView.getRowSetIterator().setRangeStart(dciterEmployeesView.getRangeSize()*empRangeIndx);
      empIndx = empRowIndx;
      }
      dciterEmployeesView.getRowSetIterator().setCurrentRow(dciterEmployeesView.getRowAtRangeIndex(empIndx));

      //additional partial target needed to be set
      AdfFacesContext.getCurrentInstance().addPartialTarget(formHandler);
     }
     }

Because the user clicks on an employee enttry only, the manage bean method parses the row key, which comes in the format of [location index, department index, employee index]

For example --- [0, 1, 13] identifies the 14th employee node of the second departments node under the first location

Knowing about the tree structure, this information is then used to determine the current row and setting it (similar to what clicking each of the commanLink does in the first example)

PLAIN TEXT

CODE:

FacesContext fctx = FacesContext.getCurrentInstance();
      ValueBinding vb = fctx.getApplication().createValueBinding("#{bindings.LocationsView1Iterator}");
      DCIteratorBinding dciterLocationsView = (DCIteratorBinding)vb.getValue(fctx);
      dciterLocationsView.getRowSetIterator().setCurrentRow(dciterLocationsView.getRowAtRangeIndex(locIndx));



The code line above sets the currentRow for the locations iterator based on the location tree node that the selected employee is a member of.

If you access ViewObjects that use page ranges, which is recommended to not always query the whole data, you must be aware of the possibility that a selected node - eg. employee node in this example - is not in the first (default) range. The range by default is set to 10 records. This setting is defined on the iterator binding properties.

To access the correct row, you need to get the range size out of the employee index. E.g. employee 23 means that it is the 4th record (counting starts by 0) in the 2nd range (also counting from zero)

PLAIN TEXT

CODE:

int empRangeSize = dciterEmployeesView.getRangeSize();
      int empRangeIndx = 0;
      int empRowIndx = 0;
      if (empIndx + 1> empRangeSize){
      empRangeIndx = empIndx/ empRangeSize;
      empRowIndx = empIndx % empRangeSize;
      dciterEmployeesView.getRowSetIterator().setRangeStart(dciterEmployeesView.getRangeSize()*empRangeIndx);
      empIndx = empRowIndx;
      }

Setting the range start ensures that the employee index (3 if the index number was 23) can be looked up

To run the application, you need to

Get the workspace from here

Copy adf-faces-impl.jar from jdeveloper_10131_3984\jlib to AdfFacesTreeSample\ViewController\public_html\WEB-INF\lib and AdfFacesTreeSample\ViewController2\public_html\WEB-INF\lib
Copy jsf-impl.jar from jdeveloper_10131_3984\jsf-ri to AdfFacesTreeSample\ViewController\public_html\WEB-INF\lib and AdfFacesTreeSample\ViewController2\public_html\WEB-INF\lib
Create a named database connection hrconn in Jdeveloper
Run TreeNode.jsp or TreeNode2.jsp