10

I've got a basic flow example working:

src/main/webapp
|
|- index.xhtml
|- flow1
   |- flow1-flow.xml
   |- flow1.xhtml

index.xhtml has a simple form that enters the flow with a parameter:

<h:form>
    Click to enter flow1
    <h:commandButton action="flow1" value="Flow 1">
        <f:param name="testInput" value="hi there"/>
    </h:commandButton>
</h:form>

flow1.xhtml displays the param and lets you enter a value into flow scope:

<h:form>
    Hi this is page 1.
    <h:inputText label="Enter something:" value="#{flowScope.testOutput}"/><br/>
    Request parameter: #{param['testInput']}<br/>
    <h:commandButton action="returnFromFlow1"/>
</h:form>

flow1-flow.xml just defines the return node as "returnFromFlow1" and sets it to /index.xhtml.

This seems to be working. I want to implement post-redirect-get when entering the flow so that the browser address bar stays in sync with the view. So I naturally tried action="flow1?faces-redirect=true". This change prevents the flow from executing.. it simply reloads index.xhtml when the button is clicked.

Then I tried action="flow1/flow1.xhtml?faces-redirect=true". This loads the page and redirects as expected, but the flow is not initialized. When I submit the form in the flow, I get an error about flowScope resolving to null.

Doing a little research, I found a tip to set the "to-flow-document-id" to force it to initialize the flow. So I added to my commandbutton. No change.

Any ideas about how to accomplish this?

Jon B
  • 471
  • 5
  • 8

3 Answers3

1

If the only requirement is that the browser address bar stays in sync with the view, you can simply use <h:button instead of <h:commandButton. This works because <h:button uses Javascript to generate a HTTP GET request to start and navigate to the flow. The ID of the flow has to be specified as value of the outcome attribute:

index.xhtml:

<h:form>
    Click to enter flow1
    <h:button outcome="flow1" value="Flow 1">
        <f:param name="testInput" value="hi there" />
    </h:button>
</h:form>

See also:


If the requirement is that some checks have to be done before the permission to start the flow is granted, you have to manually initialize and navigate to the flow as follows:

index.xhtml:

<h:form>
    Click to enter flow1
    <h:commandButton action="#{backingBean.startFlow1()}" value="Flow 1" />
</h:form>

flow1.xhtml:

<h:form>
    Hi this is page 1.
    <h:inputText label="Enter something:" value="#{flowScope.testOutput}"/><br/>
    Request parameter: #{flowScope.testInput}<br/>
    <h:commandButton action="returnFromFlow1"/>
</h:form>

BackingBean:

public void startFlow1() {
    if (!permissionGranted) {
        // show "permission denied"
        return;
    }

    final FacesContext facesContext = FacesContext.getCurrentInstance();
    final Application application = facesContext.getApplication();

    // get an instance of the flow to start
    final String flowId = "flow1";
    final FlowHandler flowHandler = application.getFlowHandler();
    final Flow targetFlow = flowHandler.getFlow(facesContext,
            "", // definingDocumentId (empty if flow is defined in "faces-config.xml")
            flowId);

    // get the navigation handler and the view ID of the flow
    final ConfigurableNavigationHandler navHandler = (ConfigurableNavigationHandler) application.getNavigationHandler();
    final NavigationCase navCase = navHandler.getNavigationCase(facesContext,
            null, // fromAction
            flowId);
    final String toViewId = navCase.getToViewId(facesContext);

    // initialize the flow scope
    flowHandler.transition(facesContext,
            null, // sourceFlow
            targetFlow,
            null, // outboundCallNode
            toViewId); // toViewId

    // add the parameter to the flow scope
    flowHandler.getCurrentFlowScope()
            .put("testInput", "hi there2!");

    // navigate to the flow by HTTP GET request
    final String outcome = toViewId + "?faces-redirect=true";
    navHandler.handleNavigation(facesContext,
            null, // from action
            outcome);
}

Please note that the parameter can not be added to the button in this case but has to be added to the flow scope instead! Also note that the redirect to the flow has to be done by NavigationHandler#handleNavigation() instead of ExternalContext#redirect() because the flow scope is terminated otherwise!

See also:

ltlBeBoy
  • 1,242
  • 16
  • 23
0

Well, if I'm reading the JSF-2.2 faces-config schema correctly, you should be able to specify a <redirect/> directive in a faces-config.xml. So using the following, you should able to achieve a redirect:

       <navigation-rule>
           <from-view-id>/pages/test/test.xhtml</from-view-id>
               <navigation-case>
                   <from-outcome>flow1</from-outcome>
                   <to-flow-document-id>flow1.xhtml</to-flow-document-id>
                       <redirect />
              </navigation-case>
       </navigation-rule>
kolossus
  • 20,559
  • 3
  • 52
  • 104
  • Unfortunately, it does not work. I've tried several possible variants of it, and didn't get any results. – T.G Oct 13 '14 at 08:00
  • Additionally, if you create a valid navigation rule to flow, and it will by applied during navigation, it will most likely cause an error with accessing uninitialized bean, because flow will not be initialized. – T.G Oct 13 '14 at 08:08
  • @T.G - that sounds like a bug because [the api specifically stipulates that `@FlowScoped` beans be instantiated upon entry into a flow by the FlowHandler](http://docs.oracle.com/javaee/7/api/javax/faces/flow/FlowHandler.html) – kolossus Oct 13 '14 at 16:33
  • @T.G - Something odd in [the source for the FlowHandler](http://grepcode.com/file/repo1.maven.org/maven2/org.glassfish/javax.faces/2.2.0/com/sun/faces/flow/FlowHandlerImpl.java?av=h#FlowHandlerImpl) is that there's nowhere in the implementation or superclass where the flowscoped bean is actually being retrieved. A `getCurrentFlowScope` is defined, but I can't find its usage. Even its definition is suspect in that according to the spec and javadoc, the expectation is that a new instance should be created on entry into a flow. – kolossus Oct 13 '14 at 16:54
  • Maybe you should post your stacktrace here – kolossus Oct 13 '14 at 17:01
  • but there is no such stack trace except entering flow document by traditional navigation rule. There is no way that someone can initialize flowscope bean manually or end its life. FacesFlow is in alpha state - that is what i always say, when someone ask me about what i'm thinking about this feature of JSF. ALPHA PHASE. `GetCurrentFlowScope` is method very usefull however, because thanks to this method i'm able to add transitions from inside of a flow, to another place in system if there is no connection so application menu don't have to be handled in every single flow definition. – T.G Oct 14 '14 at 06:34
  • You could always write a custom FlowHandler @T.G – kolossus Oct 14 '14 at 11:47
  • i've tried that long time ago. it adds redirect param to flow id or node id, but it get lost during processing later by framework code. Im saying to you - ALPHA PHASE – T.G Oct 14 '14 at 12:46
  • I guess a bug filing is in order @T.G – kolossus Oct 14 '14 at 14:56
0

Please run this example i hope it will help you out.

<navigation-rule>
    <navigation-case>
        <from-outcome>flow1</from-outcome>
        <to-view-id>flow1.xhtml</to-view-id>
        <redirect></redirect>
    </navigation-case>
</navigation-rule>
Umair
  • 860
  • 2
  • 13
  • 30