Pages

Sunday, March 29, 2009

Mainpage and Task Flow region interaction

In JDeveloper 11g a Task Flow region can exists isolated in your ADF page, but there are ways to interact with this region. For example you can use input parameters like I did in this example or you can pass ADF pagedef events to the region like I did in this example. In this blog I will use queueActionEventInRegion method to start a navigation action in the Task Flow from the mainpage so it goes to the next page fragment and I will use a regionNavigationListener on the region to get the current displayed page fragment of the region so I can use this in the mainpage to disable some buttons. ( Thanks David Giammona for the great region interaction paper ).
Here some pics of the example I made. In the mainpage I have two buttons with these buttons I pass a event to the region so it goes to the next pagefragment. When this happens the regionNavigationListener is fired and the new displayed view is passed on the mainpage so I can disable the right button.




The bounded Task Flow I use for this example with two pages and some control flow cases between these two fragments
Here is the mainpage jsf page code with the regionNavigationListener on the af:region component.

<?xml version='1.0' encoding='windows-1252'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
<jsp:directive.page contentType="text/html;charset=windows-1252"/>
<f:view>
<af:document>
<af:form>
<af:panelHeader text="Main page">
<f:facet name="context">
<af:panelGroupLayout layout="horizontal">
<af:outputLabel value="Old view"/>
<af:outputText value="#{Main.oldRegionView}"
id="old"/>
<af:spacer width="50" height="1"/>
<af:outputLabel value="New view"/>
<af:outputText value="#{Main.newRegionView}"
id="new"/>
</af:panelGroupLayout>
</f:facet>
<f:facet name="legend">
<af:toolbar>
<af:commandToolbarButton text="Page1"
id="buttonPage1"
actionListener="#{Main.pageOneActionListener}"
disabled="#{Main.newRegionView eq '/region-task-flow/page1'}"/>
<af:commandToolbarButton text="Page2"
id="buttonPage2"
actionListener="#{Main.pageTwoActionListener}"
disabled="#{Main.newRegionView eq '/region-task-flow/page2'}"/>
</af:toolbar>
</f:facet>
<af:panelBox text="Task Flow Region" showDisclosure="false">
<f:facet name="toolbar"/>
<af:region value="#{bindings.regiontaskflow1.regionModel}"
id="regio1"
binding="#{Main.region}"
regionNavigationListener="#{Main.navigationListener}"/>
</af:panelBox>
</af:panelHeader>
</af:form>
</af:document>
</f:view>
</jsp:root>

and here is backing bean code where I pass a queueActionEventInRegion event so it navigates to the other page fragment.

package nl.whitehorses.tf.region.view.backing;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.MethodExpression;

import javax.faces.application.Application;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.event.PhaseId;

import oracle.adf.view.rich.component.rich.fragment.RichRegion;
import oracle.adf.view.rich.context.AdfFacesContext;
import oracle.adf.view.rich.event.RegionNavigationEvent;

public class MainPage {
private RichRegion region;
private String OldRegionView;
private String NewRegionView;
private String outcome;

public MainPage() {
}

public void pageOneActionListener(ActionEvent actionEvent) {
outcome = "to1";
region.queueActionEventInRegion(getMethodExpression("#{Main.getOutcomeExpression}"),
null, null, false, -1, -1,
PhaseId.ANY_PHASE);
}

public void pageTwoActionListener(ActionEvent actionEvent) {
outcome = "to2";
region.queueActionEventInRegion(getMethodExpression("#{Main.getOutcomeExpression}"),
null, null, false, -1, -1,
PhaseId.ANY_PHASE);
}

private MethodExpression getMethodExpression(String name) {
FacesContext facesCtx = FacesContext.getCurrentInstance();
Application app = facesCtx.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesCtx.getELContext();
return elFactory.createMethodExpression(elContext, name, String.class, new Class[] { });
}


public String getOutcomeExpression() {
return outcome;
}

public void navigationListener(RegionNavigationEvent event) {
NewRegionView = event.getNewViewId();
OldRegionView = event.getOldViewId();
AdfFacesContext.getCurrentInstance().addPartialTarget(getUIComponent("old"));
AdfFacesContext.getCurrentInstance().addPartialTarget(getUIComponent("new"));
AdfFacesContext.getCurrentInstance().addPartialTarget(getUIComponent("buttonPage1"));
AdfFacesContext.getCurrentInstance().addPartialTarget(getUIComponent("buttonPage2"));
}

private UIComponent getUIComponent(String name) {
FacesContext facesCtx = FacesContext.getCurrentInstance();
return facesCtx.getViewRoot().findComponent(name);
}

public void setRegion(RichRegion region) {
this.region = region;
}

public RichRegion getRegion() {
return region;
}

public String getOldRegionView() {
return OldRegionView;
}

public String getNewRegionView() {
return NewRegionView;
}

}

Here is the example workspace.

15 comments:

  1. Hi Edwin,
    I try to use queueActionEventInRegion as you describe and it works!

    Then, i try to use queueActionEvent to navigate to a methodCall in my region
    (i call "next" in this image :
    http://www.cijoint.fr/cj200904/cij1toWBKG.jpg )

    The methodCall call "myBeanMethod" located in a Bean (scope : pageFlow) :

    public String myBeanMethod() { System.out.println(BindingContext.getCurrent().getCurrentBindingsEntry());
    //display null
    }

    The problem is that i can't access to the binding : BindingContext.getCurrent().getCurrentBindingsEntry(), return null!

    When i call this method from a button in my region (so, without using queueActionEvent) : BindingContext.getCurrent().getCurrentBindingsEntry() is not null.

    How can i access to the binding of my jsff using queueActionEvent?

    Thanks for your help

    Clément

    ReplyDelete
  2. Hi,

    maybe you can do this. get the binding of the mainpage and get the taskflow region in the pagedef.

    get the executablesList and the get right JUFormBinding. this is the same as the bindingContainer.

    # // main jsf page
    # DCBindingContainer dc = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
    # // taskflow binding
    # DCTaskFlowBinding tf = (DCTaskFlowBinding)dc.findExecutableBinding("dynamicRegion1");
    # // pagedef of a page fragment
    # JUFormBinding form = (JUFormBinding) tf.findExecutableBinding("regions_employee_regionPageDef");
    # // handle to binding container of the region.
    # DCBindingContainer dcRegion = form;

    thanks Edwin

    ReplyDelete
  3. Hi Edwin,
    The solution you gave me works : but, i would like to call a method in the task flow bean, et not a method in the main page.
    Your solution : JSP->button->JSPBean->method in JSP call a executable in JSF

    What i would like : JSPT->button->QueueAction->Method in JSF call a executable in JSF

    The reason is that i would like to reuse the task flow, and the method : not to rewrite the method all the time i reuse the task flow.

    Thanks for your works

    ReplyDelete
  4. Hi,

    I saw your picture. can you make a control flow case between the view and the router and call thisn from the main page

    thanks

    ReplyDelete
  5. Hi Edwin,
    I can call a control flow case to a router from the main page using the queueActionEvent :
    http://www.cijoint.fr/cj200905/cij7UByFyl.jpg

    It works.But, i don't understand what you are thinking about.

    Thanks

    ReplyDelete
  6. Ok , nice.

    Can you explain this a little further

    What i would like : JSPT->button->QueueAction->Method in JSF call a executable in JSF

    ReplyDelete
  7. Hi Edwin,
    To be more clear, i made a picture : A picture is worth a thousand words : http://www.cijoint.fr/cj200905/cijw3N69KI.jpg

    1.My JSPX call a method on the action button.
    2.This method, which is in the bean of my jspx, use queueActionEvent to call a navigation in the region (the task Flow).
    3.This navigation go to a methodCall, which call a method in the bean of the taskFlow.
    4.This method execute a methodAction in the binding of a JSFF.
    5.This method return the next navigation of my taskFlow.

    Today, i can't do the point4 :
    BindingContext.getCurrent().getCurrentBindingsEntry() return null.

    I hope it's more clear.

    Thanks for your help

    Clément

    ReplyDelete
  8. Ok, Now I know your problem.

    When you start the action with a button in the taskflow is then currentBindingC. not null.

    I think that the queueaction does something special.

    Do you have a testcase.

    ReplyDelete
  9. Hi Edwin,

    I've set up something similar to the above (I'm using links instead of buttons in my version), you can see my task flow at http://img222.imageshack.us/my.php?image=layoutx.jpg. I'm having trouble with this line:

    MethodExpression me = ef.createMethodExpression(elc, "#{backingBeanScope.backing_index.getOutcomeExpression}", String.class, new Class[] { });

    If I click any of my links it changes the region to the 'view' fragment and then doesn't respond to any more link clicks. If I modify the above to:

    MethodExpression me = ef.createMethodExpression(elc, outcome, String.class, new Class[] { });

    ...Then it works as expected (although I can go from the template to view and welcome fragments when I shouldn't be able to should I?) - do you know what might be wrong with the "#{backingBeanScope.backing_index.getOutcomeExpression}" part?

    Thanks

    ReplyDelete
  10. Hi

    Can you change the backingBeanScope to pageFlowScope or Session and then see what happens.

    thanks Edwin

    ReplyDelete
  11. Hi Clément,

    I am now looking at your case and a methodcall in a taskflow does not a bindingcontainer unless you give this methodcall a pagedef. So you can't use BindingContext.getCurrent().getCurrentBindingsEntry() when you don't have pagedef. and the methodcall is running in its own area and in the scope of the fragment so you can't get this bindingcontainer with getCurrentBindingsEntry

    use findBindingContainer to lookup the right bindingcontext.

    Now I am looking for a solution.

    thanks Edwin

    ReplyDelete
  12. Hi Clement.

    I won't use a java class for the methodcall in TF. First generate an ADF datacontrol on it and drop this in the Task Flow . when your method needs data then add parameters to this method then you can use ${data.xxxPageDef.xxxIterator.currentRow}} to get the data of the page and do your thing.

    thanks Edwin

    ReplyDelete
  13. Hi Edwin,

    I try to use findBindingContainer to lookup the right bindingcontext, and it works :

    BindingContext bc = BindingContext.getCurrent();

    DCBindingContainer container= bc.findBindingContainer(pIdPageDefInDataBinding);

    The second solution (methodCall for a method exposed in dataControl) is good too!

    Thanks for your help!

    I made a scheme for "taskFlow communications in region" :

    http://www.cijoint.fr/cj200905/cijXwDvPdD.jpg

    Clément

    ReplyDelete
  14. Edwin,

    Is there any way I can interact with the bound region to move through the task flow without refreshing the whole JSP parent page? I have a control on the parent page that doesn't like being re-initialised.

    Thank-you,

    Adam

    ReplyDelete