Friday, January 25, 2013

Spring Webflow: Embedding a flow in a modal JSF dialog

Introduction

The latest version of Spring Webflow (2.3.1) provides us with a new and very interesting functionality that I have really missed in my current project: embedded flows. By default, Webflow applies the POST/REDIRECT/GET pattern every time it enters a view state. The reason to do this is to prevent duplicate form submissions, but this also prevents us from having two different view states on the same page.

With embedded flows, we have the possibility to execute transitions and load view states via Ajax requests, avoiding a full page render. Thanks to the Spring samples repository, I've had the opportunity to see how to embed a flow on a page. In this tutorial, I will explain how to apply the example within a modal dialog.

You can get this tutorial source code at github: https://github.com/xpadro/spring-webflow


The flow

The demo application consists of a main flow called 'sales-flow', which shows the sales made by a specified employee. The employee information view contains an access to a subflow called 'employee-flow' that loads the employee data. This subflow will fully execute within a modal dialog.




How it works

In order to execute a flow in embedded mode, you simply need to add a parameter to the request url. For example:


                http://localhost:8080/myApp/myFlow?mode=embedded


As we need to call the subflow from the main sales flow, we can also do it by defining an input attribute.

In this example, the main flow calls the subflow using an input attribute:

      <view-state id="main">
            <transition on="start" to="embedded-flow"/>
            <transition on="next" to="sales"/>
      </view-state>
     
      <view-state id="sales">
            <transition on="back" to="main"/>
      </view-state>

      <subflow-state id="embedded-flow" subflow="sales-flow/employee-flow">
            <input name="mode" value="'embedded'"/>
            <transition on="final" to="main"/>
      </subflow-state>



And the embedded flow:

      <action-state id="getDataAction">
            <evaluate expression="getDataController"/>
            <transition on="yes" to="confirmation"/>
      </action-state>
     
      <view-state id="confirmation">
            <transition on="reset" to="reset"/>
            <transition on="confirm" to="final"/>
      </view-state>

      <view-state id="reset">
            <transition on="getData" to="getDataAction"/>
      </view-state>
     
      <end-state id="final"/>


To load the embedded flow on the main page of the sales flow, I use the dialog component from the Primefaces library:

<p:dialog widgetVar="employeeSearch" header="Search employee" modal="true" width="580">
   <p:outputPanel>
      <h:form id="mainForm">
         Insert employee name and surname: <br /><br />
         Name: <h:inputText value="#{userBean.name}" />
         Surname: <h:inputText value="#{userBean.surname}" /><br /><br />
         <p style="text-align:center">
            <p:commandButton value="Get data" action="start" update="mainForm"/>
         </p>
      </h:form>  
   </p:outputPanel>
</p:dialog>

The commandButton component will execute the action 'start' that will start the embedded flow as a subflow. The main view of the embedded flow is as follows:

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
                xmlns:ui="http://java.sun.com/jsf/facelets"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:f="http://java.sun.com/jsf/core"
                xmlns:p="http://primefaces.org/ui"
                template="/WEB-INF/layouts/standard.xhtml">

<ui:define name="content">
   <h:form id="mainForm">
      Employee data:<br />
      <p:fieldset>
        <h:outputLabel style="font-family:bold" for="name">Name: </h:outputLabel>
        <h:outputText id="name" value="#{userBean.name}"/><br/>
        <h:outputLabel style="font-family:bold" for="surname">Surname: </h:outputLabel>
        <h:outputText id="surname" value="#{userBean.surname}"/>
      </p:fieldset>
           
      <br/>
      Additional info:<br />
      <p:fieldset>
        <h:outputLabel style="font-family:bold" for="code">Code: </h:outputLabel>
        <h:outputText id="code" value="#{userBean.code}"/><br/>
        <h:outputLabel style="font-family:bold" for="city">City: </h:outputLabel>
        <h:outputText id="city" value="#{userBean.city}"/>
      </p:fieldset>

      <p>
        Confirm selection?
      </p>
      <p style="text-align:center">
        <p:commandButton value="Reset" action="reset" update="mainForm"/>
        <p:commandButton value="Confirm" action="confirm" update="mainForm" />  
      </p>
   </h:form>
</ui:define>
</ui:composition>

Keep in mind that the form must have the same id attribute in all the embedded flow views. Otherwise, it won't be possible to execute the embedded flow.



1 comment:

  1. Is this possible to keep both forms in different xhtml files?

    ReplyDelete