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


Wednesday Nov 09, 2011

JSF 2.0 Preemptive Navigation in ADFc of JDeveloper 11.1.2

Preemptive navigation is a new feature in JavaServer Faces 2.0 and allows runtime introspection of control flow cases for their target view.The JSF API for this is the ConfigurableNavigationHandler class that exposes the following methods

  •  getNavigationCase(FacesContext context, 
                      java.lang.String fromAction, 
                      java.lang.String outcome) 
  • getNavigationCases() – returns a Map<String, Set<NavigationCase>> that lists all available navigation cases with the viewId as the map keys

  • performNavigation(java.lang.String outcome) – Navigates to the next view based on the outcome. Developers using this method must ensure it is used during JSF InvokeApplication phase as it cannot be used any later

The NavigationCase class wraps the information defined for a navigation, including the condition (also a new feature in JSF 2.0) in which the navigation case is valid.

Preemptive navigation can be used in an application to populate redirect components, like the goLink shown below with a target view, or for redirects in a managed bean, for which developers need to know the target view. The sample below shows an ADF Faces goLink pointing to a managed bean. The managed bean returns the redirect URL for the link to follow when clicked on.

Preemptive Navigation

 The managed bean accesses the NavigationHandler defined for the JSF instance and verifies it to be an instance of ConfigurableNavigationHandler before it looks up the target viewId for the control flow case.

Note that ADFc in Oracle JDeveloper 11g R2 also supports conditional navigation, in which case developers can define an EL expression on the control flow case, using the Property Inspector, that determines when a navigation case is valid and when it is not.

Conditional navigation

Also note that Preemptive navigation fails with a NullPointer exception if the referenced control flow case is conditionally set to disabled. To handle this, the managed bean code above needs to be surrounded with a try…catch block.

Wednesday Jun 22, 2011

JDeveloper 11.1.2 : Command Link in Table Column Work Around

 Just figured that in Oracle JDeveloper 11.1.2, clicking on a command link in a table does not mark the table row as selected as it is the behavior in previous releases of Oracle JDeveloper. For the time being, the following work around can be used to achieve the "old" behavior:

To mark the table row as selected, you need to build and queue the table selection event in the code executed by the command link action listener. To queue a selection event, you need to know about the rowKey of the row that the command link that you clicked on is located in. To get to this information, you add an f:attribute tag to the command link as shown below

<af:column sortProperty="#{bindings.DepartmentsView1.hints.DepartmentId.name}" sortable="false"
   headerText="#{bindings.DepartmentsView1.hints.DepartmentId.label}" id="c1">
  <af:commandLink text="#{row.DepartmentId}" id="cl1" partialSubmit="true"
      actionListener="#{BrowseBean.onCommandItemSelected}">
    <f:attribute name="rowKey" value="#{row.rowKey}"/>
  </af:commandLink>
  ... 
</af:column>

The f:attribute tag references #{row.rowKey} wich in ADF translates to JUCtrlHierNodeBinding.getRowKey(). This information can be used in the command link action listener to compose the RowKeySet you need to queue the selected row. For simplicitly reasons, I created a table "binding" reference to the managed bean that executes the command link action. The managed bean code that is referenced from the af:commandLink actionListener property is shown next:

public void onCommandItemSelected(ActionEvent actionEvent) {

  //Do here what the command link is supposed to do
  // …  

   //In the following, set the current row  to the row that the command
   //link the user pressed is in

  //get access to the clicked command link 
  RichCommandLink comp = (RichCommandLink)actionEvent.getComponent();
  //read the added f:attribute value
  Key rowKey = (Key) comp.getAttributes().get("rowKey");
 
  //get the current selected RowKeySet from the table
  RowKeySet oldSelection = table.getSelectedRowKeys();
  //build an empty RowKeySet for the new selection
  RowKeySetImpl newSelection = new RowKeySetImpl();
  
  //RowKeySets contain List objects with key objects in them
  ArrayList list = new ArrayList();
  list.add(rowKey);
  newSelection.add(list);
  
  //create the selectionEvent and queue it
  SelectionEvent selectionEvent = new SelectionEvent(oldSelection, newSelection, table);
  selectionEvent.queue();   

  //refresh the table
  AdfFacesContext.getCurrentInstance().addPartialTarget(table);
}

Update 07/2011

While the solution above works nice for links that don't navigate off the page, it doesn't work well in a master-detail scenario where the detail collection is displayed on a separate page. To cover this use case I provided an ADF sample workspace that uses EJB and TopLink as a model. The sample shows two use cases that both show on the second page:

1. The employee data displayed on the ViewEmployees.jsf page is read from a dependent collection of the departmentsFindAll collection

2. The employee data displayed on the ViewEmployees.jsf page is read from the return of a finder method getEmployeesFindbydepartmentId.

 The changed code is shown below:

public void onDepartmentsRowSelection(ActionEvent actionEvent) {
  //Set the current row  to the row that the command
  //link the user pressed is in get access to the clicked
  //command link

  //USE CASE 1 ---- DEPENDENT COLLECTION  (departmentsFindAll

  //setting the row currency for the departments will then
  //implicitly query dependent childs in table 1 on pae 2
  RichCommandLink comp = (RichCommandLink)actionEvent.getComponent();
  //read the added f:attribute value
  Key rowKey = (Key) comp.getAttributes().get("rowKey");
  //table sis parent/parent of link
  RichTable table = (RichTable) comp.getParent().getParent();
  CollectionModel model = (CollectionModel) table.getValue();
  JUCtrlHierBinding treeBinding = (JUCtrlHierBinding)model.getWrappedData();
  DCIteratorBinding iterator = treeBinding.getDCIteratorBinding();
  iterator.setCurrentRowWithKey(rowKey.toStringFormat(true));  
 
  //USE CASE 2 ---- INDEPENDENT COLLECTION 

  //prepare attribute for page navigation. This attribute is looked up by
  //the getEmployeesFindByDepartmentId finder method on page 2
  ADFContext adfCtx = ADFContext.getCurrent();
  Map pageFlowScope = adfCtx.getPageFlowScope();
  pageFlowScope.put("rowKey", rowKey.getAttribute(0));    
}

The sample handles both cases in the managed bean that handles the action event on the command link in the table. You can download the workspace from here:

http://blogs.oracle.com/jdevotnharvest/resource/AdfEjbSample.zip

Open the sample application in JDeveloper 11.1.2.0 and change the database connection to a database of yours that has the HR schema installed and enabled.

Note: Once the mentioned selection problem is fixed, this sample is no longer required other than to learn how to access a command link and its parent table from a managed bean. Use case 1 then will work out-of-the-box using pure declarative development gestures.


        
    
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