Taskflows and regions in ADF provide a mechanism to encapsulate a set of navigation cases between pages and transactions as well as supporting managed beans that contain domain specific functions or logic. Ultimately, the encapsulation of these powerful features provides us with a facility for reusing and sharing segments of an ADF Fusion application. However, there are some occasions where encapsulation becomes a bit cumbersome, creating a need to break the well-regarded practice of encapsulation. The need to force navigation in a containing region from a nested reason is one such case. Let's further detail this example. We will begin with a bounded taskflow (as depicted below) that contains a default view activity named Parent, a second arbitrary view activity --in this case FinishLine--, and a navigation case to link the two views.
parent-task-flow.xml
In this specific flow, which we will call parent-task-flow, the Parent view activity contains a region that displays a taskflow, named child-task-flow, which contains a single view activity, labeled Child, definition that simply renders an <af:commandButton>. The button rendered by the Child view activity in the nested flow is used to queue an ActionEvent on a button hidden in the Parent view activity defined in the parent-task-flow. By queuing the event on the hidden button in the Parent view activity, the nested taskflow forces the invocation of an Action, which in our case exercises the navigation case "next" defined in parent-task-flow, and results in the navigation from the Parent view activity to the FinishLine view activity in the parent-task-flow.
child-task-flow.xml

From an end-user's perspective, the perceived interaction after clicking the button within the nested taskflow to invoke an action in the parent or containing region will appear to be a simple navigation from the Parent view activity to the FinishLine view activity. The image below illustrates how the example is rendered and demonstrates the flow from the Parent to the FinshLine view activities after the button labeled "Click to Refresh Parent" is selected. Borders have been added to the sample to better depict the boundaries of the regions that render the child-task-flow and the parent-task-flows.
Now that we understand our intended outcome, let's take a look at how we queue an action from a nested taskflow on a button hidden in the containing or parent page. For simplicity, the figure below shows the hidden the hidden button in the Parent view activity. Thus, the button labeled "Click to Refresh Parent" will queue an action and, ultimately, invoke the action declared on the button labeled "Button Executed By Child".
The key to achieving this interaction is a mechanism to pass the component id of the "Button Executed By Child" as a String to the child-task-flow so that the "Click to Refresh Parent" button can use the identifier to locate the button in the containing region and, subsequently, queue an event. To pass the component id we must define an input-parameter-definition on the child-task-flow, which will store the id on the local pageFlowScope with the key "CommandButtonToExecute" and add a parameter to the parameter-map of the executable in the pageDef that defines an instance of the child-task-flow, which in our case is called "childtaskflow1".
From child-task-flow.xml
XML:
-
<input-parameter-definition>
-
<name>commandButtonId</name>
-
<value>#{pageFlowScope.CommandButtonToExecute}</value>
-
</input-parameter-definition>
From force_navigation_in_parent_region_parentPageDef.xml
XML:
-
<taskFlow id="childtaskflow1"
-
taskFlowId="/force_navigation_in_parent_region/task_flows/child-task-flow.xml#child-task-flow"
-
xmlns="http://xmlns.oracle.com/adf/controller/binding">
-
<parameters>
-
<parameter option="required" evaluate="eachUse" id="commandButtonId" value="#{'parenttaskflow1:buttonToExecute'}"
-
xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
-
</parameters>
-
</taskFlow>
Next, we need to define an ActionListener on the "Click to Refresh Parent" button that will extract the component id from the pageFlowScope, locate the "Button Executed By Child" in the parent region, and queue an ActionEvent. The code used to define the ActionListener is listed below and is declared in a managed bean in the child-task-flow with a scope of request. The comments provided in the code snippet provide a detailed description of each line of code.
JAVA:
-
public void executeAcitonOnCommandButton
(ActionEvent evt
){
-
// -- Get the current instance of the AdfFaces context object
-
AdfFacesContext adfFacesCtx = AdfFacesContext.getCurrentInstance();
-
// -- Get the id of the button to execute from the PageFlowScope
-
String commandButtonId =
(String)adfFacesCtx.
getPageFlowScope().
get("CommandButtonToExecute");
-
// -- Create a callback to execute on the target button or component
-
ContextCallback executeCommandButtonAction = new ContextCallback() {
-
public void invokeContextCallback(FacesContext ctx, UIComponent c) {
-
// -- Cast the component to an RichCommandButton
-
RichCommandButton buttonToExecute = (RichCommandButton)c;
-
// -- Create a new action to add to the components event queue
-
-
// -- Queue the event
-
buttonToExecute.queueEvent(actionEvt);
-
}
-
};
-
// -- Get current instance of the Faces context object
-
FacesContext facesCtx = FacesContext.getCurrentInstance();
-
// -- Get view root for current view or page
-
UIViewRoot root = facesCtx.getViewRoot();
-
// -- Invoke the executeCommandButtonAction CallBack
-
boolean found = root.invokeOnComponent(facesCtx, commandButtonId, executeCommandButtonAction);
-
// -- Throw exception if the target component is not found
-
if (!found) throw new FacesException(commandButtonId + " not found!");
-
}
Conclusion
Though the interactions in this example are bit complex to describe the code required to achieve this use-case is relatively simple, and negates the need to leverage more complex features like contextual events, which could be used as a secondary approach.
Download Example
ADF Faces Rich Client, Examples, Uncategorized