Technology: ADF11g
Developed in: JDeveloper 11.1.2.1.0
Browsers tested: Internet Explorer 8.0.7601.17514 and Firefox 7.0.1
Used database schema: HR
Used tables: EMPLOYEES, DEPARTMENTS, LOCATIONS and COUNTRIES
Summary
I received the question to help out how to display different pages for each child node that is clicked in an af:tree. In this blog a solution is provided for this question.
I choose for a solution that uses a dynamic region which is refreshed every time a node is selected in the tree.
The tree I created displays all departments and the subtree all employees for that department, if a department is selected I render in the region a page contianing 'nothing here'. If a employee underneath a department is selected and the department has a manager, the information of the manager is displayed, and if the department does not have a manager its location details are displayed.
For this blog I updated some data in the database: Employee ID 116 belongs to department 130.
update employees set department_id = 130 where employee_id = 116;
Model
For this blog the EMPLOYEES, DEPARTMENTS, LOCATIONS and COUNTRIES tables of the HR schema are used.
Entities
Create the following entities:
Entity name | Based on table of HR schema | Customizations made |
---|---|---|
Employee | EMPLOYEES | None |
Department | DEPARTMENTS | None |
Country | COUNTRIES | None |
Locations | LOCATIONS | None |
Two associations have been created:
- Between Department and Employee
- Between Country and Location
View objects
Create the following view objects and create a view object and view row class for all of the view objects:
View object name | Based on entities | Customizations made |
---|---|---|
DepartmentsView | Department | None |
EmployeesView | Employee | None |
LocationsView | Location and Country | Bind variable b_id of type Integer, the where clause is extended with Location.LOCATION_ID = :b_id |
ManagersView | Employee | Bind variable b_id of type Integer, the where clause is extended with Employee.EMPLOYEE_ID = :b_id |
One view link is created:
- Between DepartmentsView and EmployeesView using the created association
Application module
An application module is created with name HrAppModule which exposes:
- DepartmentsView
- EmployeesView (using the created view link)
- LocationsView
- ManagersView
ViewController
The model layer is finished so we can start designing the pages.
In the unbounded taskflow the main page is created this page will contain the tree and a region, in this region different bounded task flows can be rendered.
Unbounded taskflow
This taskflow contains the main page (jsf) and defines two different beans.
Tree.jsf
The main page is called Tree.jsf in this page I created a panelGroupLayout with horizontal layout. The first component in this panelGroupLayout is the tree.
The tree is created by drag and drop the DepartmentsView and choosing Tree - ADF tree.
In the Edit Tree Binding dialog that pops up add (with the green plus sign) the second level to display the employees. For the departments I set the DepartmentName as display attribute and for the Employees view the FirstName and LastName.
In the created af:tree tag add a selectionListener:
<af:tree value="#{bindings.DepartmenstView.treeModel}"
var="node"
selectionListener="#{hrTreeBean.selectionListener}"
rowSelection="single"
id="t1">
In the unbounded task flow define the managed bean:
Managed bean property | Value |
---|---|
Name | hrTreeBean |
Class | nl.capgemini.marianneHorsch.adfTree.view.beans.TreeBean |
Scope | Request |
In the implementation of the bean we define the selectionListener method, in this method we check:
- Is only a department selected
- Has the selected department (or if a employee is selected the department the employee is in) a manager.
package nl.capgemini.marianneHorsch.adfTree.view.beans;
import java.util.Iterator;
import java.util.List;
import javax.faces.context.FacesContext;
import nl.capgemini.marianneHorsch.adfTree.model.services.HrAppModuleImpl;
import nl.capgemini.marianneHorsch.adfTree.model.views.DepartmenstViewRowImpl;
import nl.capgemini.marianneHorsch.adfTree.model.views.EmployeesViewRowImpl;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCDataControl;
import oracle.binding.BindingContainer;
import oracle.jbo.Key;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.trinidad.event.SelectionEvent;
public class TreeBean {
private static final Log log = LogFactory.getLog(TreeBean.class);
public TreeBean() {
super();
}
public void selectionListener(SelectionEvent selectionEvent) {
Key departmentKey = null;
Key employeeKey = null;
if (selectionEvent.getAddedSet() != null) {
Iterator iter = selectionEvent.getAddedSet().iterator();
while (iter.hasNext()) {
List objList = (List)iter.next();
if (objList != null && !objList.isEmpty()) {
Object objKey = objList.get(0);
if (objKey instanceof Key) {
departmentKey = (Key)objKey;
}
}
if (objList.size() > 1) {
Object objKey = objList.get(1);
if (objKey != null && objKey instanceof Key) {
employeeKey = (Key)objKey;
}
}
log.debug("Department key: " + (departmentKey == null ? "
log.debug("Employee key: " + (employeeKey == null ? "
}
}
if (departmentKey != null) {
DepartmenstViewRowImpl row =
(DepartmenstViewRowImpl)getService().getDepartmenstView().getRow(departmentKey);
getService().getDepartmenstView().setCurrentRow(row);
}
if (employeeKey != null) {
EmployeesViewRowImpl row =
(EmployeesViewRowImpl)getService().getEmployeesView().getRow(employeeKey);
getService().getEmployeesView().setCurrentRow(row);
}
if (employeeKey == null) {
log.debug("No employee selected.");
} else {
DepartmenstViewRowImpl row =
(DepartmenstViewRowImpl)getService().getDepartmenstView().getCurrentRow();
if (row.getManagerId() == null) {
log.debug("NO manager.");
} else {
log.debug("There is a manager.");
}
}
}
private HrAppModuleImpl getService() {
DCBindingContainer bc =
(DCBindingContainer)FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{bindings}", BindingContainer.class);
if (bc == null) {
return null;
}
DCDataControl dc = bc.findDataControl("HrAppModuleDataControl");
if (dc == null) {
return null;
}
return (HrAppModuleImpl)dc.getDataProvider();
}
}
In the example I distinguish 3 situations:
- If a department is selected I render a taskflow displaying nothing.
- If a employee is selected that belongs to a department that has a manager the manager is displayed.
- If a employee is selected that belongs to a department that does not have a manager the location of the department is displayed.
Empty flow
The empty flow is a bounded taskflow with a page fragment that only has an output text saying Nothing here.
The taskflow shares the data control with the calling taskflow.
Manager flow
The manager flow is a bounded taskflow with a page fragment that only contains a simple read only form for the ManagersView (by drag and drop from the data controls palette).
The taskflow shares the data control with the calling taskflow.
Location flow
The location flow is a bounded taskflow with a page fragment that only contains a simple read only form for the LocationsView (by drag and drop from the data controls palette).
The taskflow shares the data control with the calling taskflow.
Now the region can be added to the Tree.jsf. Drag and drop the EmptyFlow from the project navigator onto the Tree.jsf (underneath the af:tree tag). Choose Dynamic Region.
A popup appears where a bean can be selected use the green plus to create a new Bean leave the input parameter map empty. This new bean should also be configured in the unbounded taskflow:
Managed bean property | Value |
---|---|
Name | regionBean |
Class | nl.capgemini.marianneHorsch.adfTree.view.beans.HrRegionBean |
Scope | PageFlow |
In the implementation of the bean class define all 3 bounded taskflows and creates methods to set the taskflow as being the current to display:
package nl.capgemini.marianneHorsch.adfTree.view.beans;
import oracle.adf.controller.TaskFlowId;
public class HrRegionBean {
private String taskFlowId = "/WEB-INF/EmptyFlow.xml#EmptyFlow";
private static final String EMPTY_TF = "/WEB-INF/EmptyFlow.xml#EmptyFlow";
private static final String LOCATION_TF = "/WEB-INF/LocationFlow.xml#LocationFlow";
private static final String MANAGER_TF = "/WEB-INF/ManagerFlow.xml#ManagerFlow";
public HrRegionBean() {
}
public TaskFlowId getDynamicTaskFlowId() {
return TaskFlowId.parse(taskFlowId);
}
public void startEmpty() {
taskFlowId = EMPTY_TF;
}
public void startLocation() {
taskFlowId = LOCATION_TF;
}
public void startManager() {
taskFlowId = MANAGER_TF;
}
}
At the end of the selectionListener method of the TreeBean add:
HrRegionBean bean = (HrRegionBean) getBeanInstance("#{pageFlowScope.regionBean}");
if (employeeKey == null) {
bean.startEmpty();
} else {
DepartmenstViewRowImpl row =
(DepartmenstViewRowImpl)getService().getDepartmenstView().getCurrentRow();
if (row.getManagerId() == null) {
getService().getLocationsView().setb_id(row.getLocationId());
getService().getLocationsView().executeQuery();
getService().getLocationsView().setCurrentRow(getService().getLocationsView().first());
bean.startLocation();
} else {
getService().getManagersView().setb_id(row.getManagerId());
getService().getManagersView().executeQuery();
getService().getManagersView().setCurrentRow(getService().getLocationsView().first());
bean.startManager();
}
}
private Object getBeanInstance(String expression) {
FacesContext fc = FacesContext.getCurrentInstance();
ELContext elctx = fc.getELContext();
ExpressionFactory elFactory = fc.getApplication().getExpressionFactory();
return elFactory.createValueExpression(elctx, expression, Object.class).getValue(elctx);
}