zondag 14 augustus 2011

How to restart a taskflow from the menu

Technology: ADF11g
Developed in: JDeveloper 11.1.1.4.0
Browsers tested: Firefox 3.6.13 and Internet explorer 7 (7.0.6002.18005)
Used database schema: HR
Used tables: EMPLOYEES, DEPARTMENTS


Summary



In the blog 'How to fix menu item navigation when using bounded taskflows' I explained how to abandon a taskflow so you could start another taskflow from the menu bar. This works fine as long as the taskflows are not started from a dynamic region.

When you use dynamic regions (so each bounded taskflow is created using page fragments) and the current application state is somewhere in a taskflow then I would expect if you use the menu bar and select the same taskflow as the application is currently in that it would restart the taskflow so it’s default start activity is executed. Without any interference this is not the case, when you use the menu and select the current taskflow nothing is happening. However if you select another taskflow there is no custom code needed to start a new taskflow.

In this blog a solution is provided to make the current taskflow restart when using the menu option.

Overview of the page flow, the red arrows indicate the current flow, the green arrow the requested behavior after clicking on the menu:



Setup example application



For this blog an example application is created based on the HR schema. The example application contains a table of employees with detail form and a departments table and detail form. The detail forms are created in a separate page. For the employees table and form a bounded task flow is created this bounded task flow is started from the menu. For the departments table and form another bounded task flow is created which is also started from the menu.

Model layer



Create the following entities:
Entity name Based on table of HR schema Customizations made
Employee EMPLOYEES None
Department DEPARTMENTS None


Create the following view objects:
View object name Based on entities Customizations made
EmployeesView Employee None
DepartmentsView Department None


Create an application module HrAppModule which exposes the EmployeesView and DepartmentsView.



Task flow




Unbounded task flow



The unbounded task flow only contains the declaration of the managed bean used for the dynamic region (scope = view):



Bounded task flow



The employees task flow and the departments task flow are bounded task flows. Both look like this (but for departments the view is DepartmentsTable and DepartmentDetails).



The next properties are set (for both bounded task flows):
Property Value
usePageFragments true (this is the default value)
Share data controls with calling task flow true




Home page



An empty page containing a default layout of 2 column and two rows (first fixed size second stretchable):



In the first row the menu is created:

<af:menuBar id="mb1">

<af:commandMenuItem id="cmi1"

text="Employees"

action="#{viewScope.dynamicRegionManager.startEmployeesTaskFlow}"

immediate="true"/>

<af:commandMenuItem id="cmi2"

text="Departments"

action="#{viewScope.dynamicRegionManager.startDepartmentsTaskFlow}"

immediate="true"/>

</af:menuBar>

In the stretchable panel the dynamic region is defined:

<af:panelGroupLayout layout="scroll"

xmlns:af="http://xmlns.oracle.com/adf/faces/rich"

id="pgl1">

<af:region id="r1"

value="#{bindings.dynamicRegion1.regionModel}"

partialTriggers="::cmi1 ::cmi2"/>

</af:panelGroupLayout>

This can be done by dragging and dropping a bounded taskflow in the center facet then the page definition is created and filled automatically and the managed bean can be selected, after dragging and dropping the partial triggers should be added manually to the region.

DynamicRegionManager



This managed bean class contains methods for each menu item and a method to get the current taskflow:

public class DynamicRegionManager {

private String taskFlowId = "/WEB-INF/EmployeesFlow.xml#EmployeesFlow";



public DynamicRegionManager() {

}



public TaskFlowId getDynamicTaskFlowId() {

return TaskFlowId.parse(taskFlowId);

}



public String startEmployeesTaskFlow() {

taskFlowId = "/WEB-INF/EmployeesFlow.xml#EmployeesFlow";

return null;

}

public String startDepartmentsTaskFlow() {

taskFlowId = "/WEB-INF/DepartmentsFlow.xml#DepartmentsFlow";

return null;

}

}


Table page



The table pages are created by drag and drop from the Data Controls. The table is dropped as ADF Read-only table with Single Row Selection checked, all columns are displayed.

For display purposes the styleclass is set:
PropertyValue
styleClassAFStretchWidth


Underneath the table two buttons are added to go to the details page and to end the current task flow:

<af:panelGroupLayout id="pg1"

layout="horizontal">

<af:commandButton id="cb1"

text="Show details"

action="detail"/>

<af:commandButton id="cb2"

text="End"

action="end"/>

</af:panelGroupLayout>


Form page



The form pages are created by drag and drop from the Data Controls. The form is dropped as ADF Form all attributes are displayed and a submit button is added.

Underneath the form in the footer two more buttons are added to navigate back to the table and to end the taskflow. A rollback operation is added (form the data control palette) on top of the back button:

<af:group id="g1">

<af:commandButton id="cb1"

text="Submit"

actionListener="#{bindings.Commit.execute}"/>

<af:commandButton id="cb3"

text="Back"

actionListener="#{bindings.Rollback.execute}"

immediate="true"

action="back">

<af:resetActionListener/>

</af:commandButton>

<af:commandButton id="cb2"

text="End"

action="end"/>

</af:group>


Fix the menu navigation



When the application described above is deployed the navigation from the menu does not work (properly) if you’re in the details page and then click on the menu entry of the same taskflow.

The reason the menu doesn’t work is that the region concludes you’re already in this taskflow so no actions are needed.

A solution to this issue is to force the refresh of the region. This can be done by binding the region to the dynamicRegionManager bean instance and whenever a menu item is clicked (the action of the dynamicRegionManager will be executed) force a refresh:
  1. Add the binding to the region tag:

    <af:region id="r1"

    value="#{bindings.dynamicRegion1.regionModel}"

    partialTriggers="::cmi1 ::cmi2"

    binding="#{viewScope.dynamicRegionManager.dynamicRegion}"/>

  2. Define the region in the DynamicRegionManager class (include accessors):

    private RichRegion dynamicRegion;

  3. Refresh the region when an action is triggered:

    public String startEmployeesTaskFlow() {

    taskFlowId = "/WEB-INF/EmployeesFlow.xml#EmployeesFlow";

    getDynamicRegion().refresh(FacesContext.getCurrentInstance());

    return null;

    }

    public String startDepartmentsTaskFlow() {

    taskFlowId = "/WEB-INF/DepartmentsFlow.xml#DepartmentsFlow";

    getDynamicRegion().refresh(FacesContext.getCurrentInstance());

    return null;

    }

Now the menu items respond like we requested, if logging is added to the initializer and finalizer methods of the bounded taskflows you can see that on refresh the finalizer of the taskflow is executed followed by the initializer. If custom actions are needed (for example of you navigate from the table to the details with a new button that does an insert of a new row it could be usefull to do a remove of this row in the finalizer so that the created new row is removed from the view object, otherwise there is a risk on a commit in a different page that this new line will be inserted in the database).

2 opmerkingen: