Thursday, 28 August 2014

Create taskFlow and region binding at run-time , show n numbers of regions using multiTaskFlow- Oracle ADF

This is very specific development requirement to create taskFlow and region binding at run-time to show n numbers of region in page
In this case initially you don't know how much regions required in page as it depends on user activity .

you can see in http://irctc.co.in/ (Indian Railways) site
when user search for a train and check berth availability then a tab is generated at run-time with berth and fare details and it continues for each action , every time a new tab with new information is generated (n- number of tab and taskFlow)

A very good article is published in Oracle Magazine (July – August 2014) by Frank Nimphius on this requirement , article contains very good description of each and every step and a sample application with whole functionality
I found it very interesting and good so giving a quick overview of TaskFlow on Fly

this functionality is implemented using multiTaskFlow element that uses taskFlow binding from managed bean , taskFlow bindings in managed bean created using TaskFlowBindingAttributes
this class is used to set all properties of taskFlow binding at runtime

See Oracle docs (TaskFlowBindingAttributes)-
Set of attributes that define a TaskFlowBinding object. The taskFlowList attribute of the multiTaskFlow element of the page definition uses a list of object of this type to describe each taskflowBinding that the multiTaskflow binding contains. The order of this list defines the order of the region objects in the multiTaskflow binding. 

So in this post i am using Departments and Employees table of HR Schema(Oracle) to create business components for Model part


Create viewCriteria in both viewObjects (Departments and Employees) to filter data using DepartmentId




Applied viewCriteria to viewObject at AM level (Edit Vo instance and shuttle viewCriteria to selected side)
After this created methods in AMImpl class to set value in both viewCriteria's bind variables


    /**Method to filter Department ViewObject
     * @param deptId
     */
    public void filterDepartmentData(Integer deptId) {
        this.getDepartments1().setNamedWhereClauseParam("BindDeptId", deptId);
        this.getDepartments1().executeQuery();

    }

    /**Method to filter Employees Data
     * @param deptId
     */
    public void filterEmployeesData(Integer deptId) {
        this.getEmployees1().setNamedWhereClauseParam("BindDeptId", deptId);
        this.getEmployees1().executeQuery();

    }

Expose both methods to use in clientInterface

Now viewController part-

Created two bounded taskFlow with one input parameter for DepartmentId (this id will be used to filter Departments and Employess viewObject)
One for Departments and Second for Employees-

First taskFlow has a .jsff (facelets) page that has Departments viewObject as a form , just to show date with navigation buttons
filterDepartmentData method of AMImpl is used as Default Activity of this taskFlow
to filter data before entering in page
 
 
 

Second taskFlow has a .jsff (facelets) page that has Employees viewObject as a table
filterEmployeesData method of AMImpl is used as Default Activity of this taskFlow
to filter data before entering in page



Now basic configuration is complete, model is ready and bounded taskFlows are ready
then create a jsf page and manged bean (pageFlowScope) in adfc-config.xml, this page will make use of these two bounded taskFlows  and managed bean is responsible to generate taskFlow binding at runtime

Next Step is to prepare manged bean to hold taskFlowBinding-
Create a List of type TaskFlowBindingAttributes and it's accessors


import oracle.adf.controller.binding.TaskFlowBindingAttributes;    
private List<TaskFlowBindingAttributes> taskFlowBinding = new ArrayList<TaskFlowBindingAttributes>(5);

    public void setTaskFlowBinding(List<TaskFlowBindingAttributes> taskFlowBinding) {
        this.taskFlowBinding = taskFlowBinding;
    }

    public List<TaskFlowBindingAttributes> getTaskFlowBinding() {
        return taskFlowBinding;
    }

Create multiTaskFlow binding in executables of pgae-

  • Open jsf page, click on bindings tab of page editor
  • click on green plus icon of executables section
  • Select ADF TaskFlow Bindings in category and select multiTaskFlow


  • click on ok and provide an unique value for id attribute and reference of List created in managed bean as value for taskFlowList attribute


  • Configuration of  multiTaskFlow for this page is complete and now time to design page to render multiple regions. For this purpose add af:ForEach and a region inside it, forEach is responsible to add regions at run-time. here a little change i am using panelTabbed  to create tab at runtime and inside tab there will be region 
  • See this xml code- here nothing simple panelTabbed and forEach iterates over to list to identify number of items and varStatus of forEach is used to create id of showDetailItem at runtime as id must be unique. regionModel is referenced from var attributes of forEach as part of taskFlowBinding 

  •  <af:panelTabbed position="above" id="pt1" partialTriggers="b1">
                <af:forEach items="#{bindings.mtf1.taskFlowBindingList}" var="multiTF" varStatus="vs">
                   <af:showDetailItem id="Tab#{vs.index+1}" text="#{multiTF.name}" partialTriggers="b1">
                      <af:region value="#{multiTF.regionModel}" id="r1#{vs.index}" partialTriggers="::b1"/>
                   </af:showDetailItem>
                </af:forEach>
             </af:panelTabbed>
    

  • Now page is ready to show unknown (n) number of regions, here i am using two list and a button on page First List is of all departments and second one is static list to select information type (if user want to see Only Departments or Departments Wise Employees) and button to execute and add taskFlow from managed bean as per selected Department and 
  • Again a map is created in managed bean to store input parameters for taskFlow and it will used while setting and creating taskFlowBinding

  •     HashMap<String, Object> tfParam = new HashMap<String, Object>();
        public void setTfParam(HashMap<String, Object> tfParam) {
            this.tfParam = tfParam;
        }
    
        public HashMap<String, Object> getTfParam() {
            return tfParam;
        }
    

  • finally we will create taskFlow binding and set properties on button click (actionEvent of button), See this code-

  •     /**Method to create and set properties in TaskFlowBinding
         * @param actionEvent
         */
        public void displayDepartmentAction(ActionEvent actionEvent) {
            //Clear List to show newly created regions only
            taskFlowBinding.clear();
            System.out.println("List Cleared -Size>" + taskFlowBinding.size());
            //Check that user selects value in both List (Uses component binding to check)
            if (deptIdBindVal.getValue() != null && showTypeBind.getValue() != null) {
                //Put value in TaskFLow Parameter map, deptIdBindVal is binding of list of departments on page
                tfParam.put("DeptIdParam", deptIdBindVal.getValue());
    
                //Only Shows Departments taskFlow, if user selectes "Only Departments"
                if ("Only Departments".equalsIgnoreCase(showTypeBind.getValue().toString())) {
                    //Setting taskFlow properties
                    TaskFlowBindingAttributes tfAttr = new TaskFlowBindingAttributes();
                    //Creating Unique Id
                    tfAttr.setId("Department_" + deptIdBindVal.getValue());
                    //See bounded taskFlow name in WEB-INF and use here
                    tfAttr.setTaskFlowId(new TaskFlowId("/WEB-INF/DepartmentsBTF.xml", "DepartmentsBTF"));
                    //This EL refers parameter map created in this managedBean
                    tfAttr.setParametersMap("#{pageFlowScope.MultiTaskFlowBean.tfParam}");
                    //Finally add taskFlow to List
                    taskFlowBinding.add(tfAttr);
                } else { // Show both Departments and Employees
                    //Creating Departments TaskFlow binding
                    TaskFlowBindingAttributes tfAttr = new TaskFlowBindingAttributes();
                    tfAttr.setId("Department_" + deptIdBindVal.getValue());
                    tfAttr.setTaskFlowId(new TaskFlowId("/WEB-INF/DepartmentsBTF.xml", "DepartmentsBTF"));
                    tfAttr.setParametersMap("#{pageFlowScope.MultiTaskFlowBean.tfParam}");
                    taskFlowBinding.add(tfAttr);
                    //Creating Employee TaskFlow biniding
                    tfAttr = new TaskFlowBindingAttributes();
                    tfAttr.setId("Employees_" + deptIdBindVal.getValue());
                    tfAttr.setTaskFlowId(new TaskFlowId("/WEB-INF/EmployeesBTF.xml", "EmployeesBTF"));
                    tfAttr.setParametersMap("#{pageFlowScope.MultiTaskFlowBean.tfParam}");
                    taskFlowBinding.add(tfAttr);
                }
            }
        }
    

  • So Application with multiTaskFlow is ready , in this post i have used these two bounded taskFlow to show in two tabs at run-time, in same way you can call multiple TFs or a single taskFlow multiple time
  • Refer this post Region Extreme: Multi-Task-Flow Binding to learn more about multiTaskFlow binding. In this Frank also told about how to use dynamic viewObject instance to show different data at run-time, because if we use same instance of viewObject in multiple taskFlow at same time then it will not maintain state for each region to it is necessary to use dynamic instance and it's binding in pageDef
  • Read more about regions and taskFlows- Using Task Flows as Regions
Thanks - Happy Learning :)