Thursday, November 18, 2010

Triggering Navigation from a Contextual Event Handler Method

ADF task flows are key building blocks for a Fusion Web Application. You can render a bounded task flow in a page using ADF region. Often a parent page may need to trigger navigation within the embedded region while responding to specific user actions. This blog post discusses such a use case scenario and a possible solution for the same.

Consider a generic case where a JSF page contains a bounded task flow added as a region. Here the requirement is that when user selects specific option displayed on the parent page, the ADF region's content(taskflow view activity) should navigate to an a new view/page. Let me try to explain this scenario with a more realistic example. Please see the following diagram. You may notice that deptemp-task-flow-definition is added as region to a page and the this page is having two buttons to edit the department and employee details. On clicking these buttons on parent page, the 'embedded' deptemp-task-flow should navigate to the corresponding edit page.



The following steps outline the implementation at a high level.

1. Defining Contextual Event and associate this with an actionListener for the command button.
2. Defining Event Consumer and perform event mapping between producer and consumer.
3. Triggering the navigation based on event payload.

In this post I'm discussing only the 3rd step from the above list. Please refer 28.7 Creating Contextual Events in Fusion Developer's Guide, in case you are not familiar with Contextual Event and it's usage.

Triggering Navigation from the Contextual Event


Please note that, as of now there is no declarative way to navigate to a different view (conditionally) from a contextual event handler method. One possibility is to handle the navigation programmatically.

Solution

JSF lets you toqueue FacesEvent on UIComponent manually. The approach I'm following in this example is driven by this idea. If you have proper navigation sequence defined in the task flow, then this is going to be an easy task. In the current example, there are two CommandButtons defined on the default view for deptemp-task-flow. One for editing dept details, and the second one for emp details. As you might have imagined, these two buttons have action defined for navigating to edit view. Now the solution is pretty simple and straight forward - queue the ActionEvent on the appropriate button component (RichCommandButton) from the contextual event handler method. This may trigger the desired navigation at proper JSF life cycle phase(Invoke Application). Please see the code snippet copied below to get a feel of this approach.


/**
 * Handler(subscriber) for the Contextual Event
 */ 
public void handleEvent(String arg) {

  String compId = null;
  if (arg.equals("editDept")) {
    compId = DEPT_EDIT_BTN_ID;
  } else if (arg.equals("editEmp")) {
    compId = EMP_EDIT_BTN_ID;
  }

  if (compId != null) {
    RichCommandButton btn = getEditButtonComponent(compId);
    ActionEvent actEvent = new ActionEvent(btn);
    actEvent.queue();
  }

}

Before winding up this discussion, let me answer one query that may arise in your minds :)
Why don't you use javax.faces.application.NavigationHandler
.handleNavigation(....
) here?

Well, this works...but this may have it's own side effects. The call to NavigationHandler.handleNavigation(...) bypasses JSF life cycle phases and forcefully triggers the navigation. So you may see some unexpected results in certain scenarios.

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS2 + HR Schema]

A glance at the implementation

The entry point for this sample application is main.jspx. This page contains two buttons('Edit Dept' and 'Edit Emp' ) on top, and a task flow(deptemp-task-flow-definition) displaying department employee details.

The deptemp-task-flow-definition at glance:

1. This task flow has 'deptEmp' view as the default activity.
2. The 'deptEmp' view contains two CommandButtons namely 'Dept Details' and 'Emp Details'. Clicking on these button would take you to corresponding edit screens. Please note that we would be using these buttons for queuing the ActionEvent to trigger the navigation from contextual event handler method.

Please run the main.jspx. When you click on 'Edit Dept' or 'Edit Emp' buttons displayed on top, the action in turn triggers contextual event('editEvent') defined on the main page. The 'deptEmp' view on the deptemp-task-flow has been registered as a subscriber for this event. As we discussed earlier, this subscriber method queues an Action Event on the CommandButton defined on the deptEmp view('Dept Details' / 'Emp Details' buttons). This result in the navigation to the editDept/editEmp view at proper phase of JSF life cycle.

4 comments:

Juan Camilo Ruiz said...

Hi Jobinesh, great post! I actually had a use case where I captures a hoover over behavior on JS and then navigated to another region. For this one the queue on JSF was the only one that worked. NavigationHandler didn't help

Here s the article, however it is in Spanish.
http://oracleradio.blogspot.com/2010/10/navegando-entre-pagefragments-de-un.html

Juan C.

Jobinesh said...

Cool..thanks Juan for sharing this.

Anonymous said...

Very Nice. Thanks a Lot.

Binu said...

Hi Jobinesh,

I got a situation as follows:

I got a three Task Flows created. ParentTaskFlow, ChildTaskFlow1, ChildTaskFlow2
In the ParentTaskFlow Jsff page I got 2 buttons

1. BUTToN1_ChildTaskFlow1
2. BUTToN2_ChildTaskFlow2

And a ROUTER to Both the child flows

So when I click on button1 it should flow to ChildTaskFlow1 which is set as 'defult outcome' for router
and it works fine.

But when I click on button2 how do i route to ChildTaskFlow2 ?

Please help me.

REgards,
Binu Mathew
binukmathew@gmail.com