2. JSP Example

The create link goes to '/person.html' which is mapped to the person flow. The search link still goes through the PersonController.

/WEB-INF/templates/menu.jsp
                
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<div id="side-bar">
    <a href="<c:url value="/"/>">Home</a>
    
    <p><fmt:message key="person.form.title"/></p>
        <a href="<c:url value="/person.html"/>"><fmt:message key="button.create"/></a> 
        <a href="<c:url value="/person/search.html"/>"><fmt:message key="button.search"/></a>
</div>
                
            

The edit link goes to the person flow (ex: '/person.html?id=1'). The flow will look up the person record based on the id request parameter.

/WEB-INF/jsp/person/search.jsp
                
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

<h1><fmt:message key="person.search.title"/></h1>

<table class="search">
    <tr>
        <th><fmt:message key="person.form.firstName"/></th>
        <th><fmt:message key="person.form.lastName"/></th>
    </tr>
<c:forEach var="person" items="${persons}">
    <tr>
        <c:url var="editUrl" value="/person.html">
            <c:param name="id" value="${person.id}" />
        </c:url>
        <c:url var="deleteUrl" value="/person/delete.html">
            <c:param name="id" value="${person.id}" />
        </c:url>

       <td>${person.firstName}</td>
        <td>${person.lastName}</td> 
       <td>
            <a href='<c:out value="${editUrl}"/>'><fmt:message key="button.edit"/></a>
            <sec:authorize ifAllGranted="ROLE_ADMIN">
                <a href='<c:out value="${deleteUrl}"/>'><fmt:message key="button.delete"/></a>
            </sec:authorize>
        </td>
    </tr>
</c:forEach>
</table>
                
            

The form:form element just needs to have it's modelAttribute set to correspond to where the flow put the Person instance. The save and cancel buttons specify which event id they are associated with for this step in the flow. This value should match the flow's transition element's on attribute.

The link to add an address goes to the address subflow. If there are any addresses, they are displayed in a table with an edit next to each record. There is also a delete link if the user is an admin.

The top of the page has a 'messages' div which can display a status message key or any Spring bind errors. This is where Spring Web Flow will store any errors for automatic validation performed based on a method on the model instance or a bean. In this case, there is a personValidator bean that validates if the first and last name are not blank.

/WEB-INF/jsp/person/form.jsp
                
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

<h1><fmt:message key="person.form.title"/></h1>

<div id="messages">
    <c:if test="${not empty statusMessageKey}">
       <p><fmt:message key="${statusMessageKey}"/></p>
    </c:if>

    <spring:hasBindErrors name="person">
        <h2>Errors</h2>
        <div class="formerror">
            <ul>
            <c:forEach var="error" items="${errors.allErrors}">
                <li>${error.defaultMessage}</li>
            </c:forEach>
            </ul>
        </div>
    </spring:hasBindErrors>
</div>

<form:form modelAttribute="person">
    <form:hidden path="id" />

    <fieldset>
        <div class="form-row">
            <label for="firstName"><fmt:message key="person.form.firstName"/>:</label>
            <span class="input"><form:input path="firstName" /></span>
        </div>       
        <div class="form-row">
            <label for="lastName"><fmt:message key="person.form.lastName"/>:</label>
            <span class="input"><form:input path="lastName" /></span>
        </div>
        <div class="form-buttons">
            <div class="button">
                <input type="submit" id="save" name="_eventId_save" value="<fmt:message key="button.save"/>" 
                    onclick="Spring.remoting.submitForm('save', 'person')"/>&#160;
                <input type="submit" name="_eventId_cancel" value="Cancel"/>&#160;          
            </div>    
        </div>
    </fieldset>
</form:form>

<c:if test="${not empty person.id}">
<div style="clear: both;float:left;">
<div>
<a href="${flowExecutionUrl}&_eventId=addAddress" ><fmt:message key="address.form.button.add"/></a>
</div>
</c:if>

<c:if test="${empty person.addresses}">
    <p>&nbsp;</p>
</c:if>

<c:if test="${not empty person.addresses}">
<table class="search">
    <tr>
        <th><fmt:message key="address.form.address"/></th>
        <th><fmt:message key="address.form.city"/></th>
        <th><fmt:message key="address.form.state"/></th>
        <th><fmt:message key="address.form.zipPostal"/></th>
        <th><fmt:message key="address.form.country"/></th>
    </tr>
<c:forEach var="address" items="${person.addresses}">
    <tr>
            <td>${address.address}</td>
            <td>${address.city}</td>
            <td>${address.state}</td>
            <td>${address.zipPostal}</td>
            <td>${address.country}</td> 
            <td>
                <a href="${flowExecutionUrl}&_eventId=editAddress&addressId=${address.id}" ><fmt:message key="button.edit"/></a>
                <sec:authorize ifAllGranted="ROLE_ADMIN">
                    <a href="${flowExecutionUrl}&_eventId=deleteAddress&addressId=${address.id}" ><fmt:message key="button.delete"/></a>
                </sec:authorize>
            </td>
    </tr>
</c:forEach>
</table>
</c:if>

</div>
                
            

The 'messages' div at the top of the page can display a status message or any Spring bind errors. The validation for address is in a method in the Address class. The method in this case is validateAddressForm(MessageContext context). It is for the 'addressForm' view-state so the method name should be 'validate' + ${viewStateId} and take a MessageContext as a parameter.

/WEB-INF/jsp/address/form.jsp
                
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

<h1><fmt:message key="address.form.title"/></h1>

<div id="messages">
    <c:if test="${not empty statusMessageKey}">
       <p><fmt:message key="${statusMessageKey}"/></p>
    </c:if>

    <spring:hasBindErrors name="address">
        <h2>Errors</h2>
        <div class="formerror">
            <ul>
            <c:forEach var="error" items="${errors.allErrors}">
                <li>${error.defaultMessage}</li>
            </c:forEach>
            </ul>
        </div>
    </spring:hasBindErrors>
</div>

<form:form modelAttribute="address">
    <form:hidden path="id" />

    <fieldset>
        <div class="form-row">
            <label for="address"><fmt:message key="address.form.address"/>:</label>
            <span class="input"><form:input path="address" /></span>
        </div>       
        <div class="form-row">
            <label for="city"><fmt:message key="address.form.city"/>:</label>
            <span class="input"><form:input path="city" /></span>
        </div>       
        <div class="form-row">
            <label for="state"><fmt:message key="address.form.state"/>:</label>
            <span class="input"><form:input path="state" /></span>
        </div>       
        <div class="form-row">
            <label for="zipPostal"><fmt:message key="address.form.zipPostal"/>:</label>
            <span class="input"><form:input path="zipPostal" /></span>
        </div>       
        <div class="form-row">
            <label for="country"><fmt:message key="address.form.country"/>:</label>
            <span class="input"><form:input path="country" /></span>
        </div>       
        <div class="form-buttons">
            <div class="button">
                <input type="submit" id="save" name="_eventId_save" value="<fmt:message key="button.save"/>" 
                    onclick="Spring.remoting.submitForm('save', 'address')"/>&#160;
                <input type="submit" name="_eventId_cancel" value="Cancel"/>&#160;          
            </div>    
        </div>
    </fieldset>
</form:form>