Saturday, February 23, 2013

SpringMVC Multi Row Form Binding and Validation

In this tutorial, I will show you how to bind and validate a multi row form, as figure bellow.


  • For binding Multi Row, I use the <spring:bind> tag lib
  • For validation, I use the manual validation (by inheriting the spring validator interface)

To start, let create these beans:


Option.java
public class Option {

    private String value;

    private String label;

...

}
This bean used in creating the list of option in select tag, the list of checkboxs....
User.java
public class User {

    // a text box

    private String name;

    // check boxs

    private String[] hobbies;

    // a radio

    private String sex;

    // options

    private String[] countries;

...

}

This bean store the information of an User

And a form bean UserForm.java
public class UserForm {

    private List<User> lstUser;

...

}

There is only one user list in this form

Modify User Form


We need these tag lib
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<%@taglib uri="http://www.springframework.org/tags" prefix="spring"%>

The form attribute is users
<form:form action="/multirow/modifyUser" method="POST" modelAttribute="users">

To loop through a list, we use the core tag lib
<c:forEach items="${users.lstUser}" var="user" varStatus="loopStatus">

As the figure above, I will show you 4 types of component

1. Text Box


<spring:bind 

    path="users.lstUser[${loopStatus.index}].name">

        <input 

            type="text" 

            name="<%=status.getExpression() %>"

            value="<%=status.getValue() %>"

            />

</spring:bind>

  • Make attention at the path of spring:bind tag lib, here we are defining a list of name attribute
  • status.getExpression() returns the html name
  • status.getValue() returns the value of name attribute

2. Multi Check box


<spring:bind path="users.lstUser[${loopStatus.index}].hobbies">

    <c:forEach items="${lstHobby}" var="hobby">

        <c:set var="checkMe" value="" />

        <c:forEach items="${status.actualValue}" var="curValue">

            <c:if test="${curValue == hobby.value}">

                <c:set var="checkMe" value="checked" />

            </c:if>

        </c:forEach>

        <input 

            type="checkbox" 

            name="<%=status.getExpression() %>"

            value="${hobby.value}"

            <c:out value="${checkMe }"/>

        />${hobby.label }

    </c:forEach>

</spring:bind>

3. Radio Button


<c:set var="male" value="M"/>

<c:set var="female" value="F"/>

<spring:bind path="users.lstUser[${loopStatus.index}].sex">

    <input 

        type="radio"

        name="<%=status.getExpression()%>"

        value="${male }"

        <c:if test="${male == status.value }">checked</c:if>

    />Male

    <input 

        type="radio"

        name="<%=status.getExpression()%>"

        value="${female }"

        <c:if test="${female == status.value }">checked</c:if>

    />Female

    <div><c:out value="${status.errorMessage }" /></div>

</spring:bind>

4. Select


<spring:bind path="users.lstUser[${loopStatus.index}].countries">

    <select name="<%=status.getExpression() %>">

        <option value="NONE">---SELECT---</option>

        <c:forEach items="${lstCountry }" var="country">

            <c:set var="selectMe" value="" />

            <c:forEach items="${status.actualValue}" var="curValue">

                <c:if test="${curValue == country.value}">

                    <c:set var="selectMe" value="selected" />

                </c:if>

            </c:forEach>

            <option 

                value="${country.value }"

                ${selectMe } 

            >

                ${country.label }

            </option>

        </c:forEach>

    </select>

</spring:bind> 

Tip
When we compare 2 variables of type String, the <c:if test> uses the equals function to compare them

That is enough for binding a mutil row form, next we discuss about validation
Here I just show you how to create a error message for an attribute in list.
public void validate(Object obj, Errors errors) {

    UserForm form = (UserForm) obj;

    List<User> lstUser = form.getLstUser();

    if (lstUser == null) {

        return;

    }

    for (int i = 0; i < lstUser.size(); i++) {

        User user = lstUser.get(i);

        if (user.getName() == null || "".equals(user.getName().trim())) {

            errors.rejectValue("lstUser[" + i + "].name", "user.name.required");

        }

    }

}

The difference with the validation of a bean is the error field name is under list format errors.rejectValue("lstUser[" + i + "].name.

To display an error message on form, we use status.errorMessage in <spring:bind>
<spring:bind path="users.lstUser[${loopStatus.index}].name">
    ...
    <span class="errors"><c:out value="${status.errorMessage }" /></span>    
</spring:bind>

Related:

References:
Source code:

4 comments:

  1. Excellent, exactly what I am looking for

    ReplyDelete
  2. You are my Savior!!! Great Article!!!

    Just change the Action URL from



    to

    ReplyDelete
  3. Hi, your example is Great. But I have one question: I was download your example a run on Tomcat, but I do not know complete URL for load th eapplication (http://localhost:8080/????????). Thanks you

    ReplyDelete
  4. Excelent, but i want to add row dynamically to the table

    ReplyDelete