Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to enter a JSF 2.2 flow with faces-redirect

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?

like image 522
Jon B Avatar asked May 19 '14 19:05

Jon B


1 Answers

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:

  • Difference between h:button and h:commandButton

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:

like image 138
ltlBeBoy Avatar answered Nov 16 '22 03:11

ltlBeBoy