October 05, 2009

Spring MVC - Dynamic list binding and validating the input data


It took quiet some time to figure out how to display a dynamic list of data on screen and get user input information back. We also had to perform validation on this dynamic list input data and display error message on the corresponding row.  
Here is the simple example to achieve the above requirement.

Consider that you need to display Parent and list of child information on a screen and get the Age and Sex information of each child. Also you need to show error message on the screen if the user has not entered the age or sex information.

1) Form bean

public class MotherFormBean{  
   private String name;  
   private List<Child> children;  
   public MotherFormBean() {  
     children= LazyList.decorate(new ArrayList(),  
       FactoryUtils.instantiateFactory(Child.class));  
   }  
 }  
 public class Child{  
   private String name;  
   private String age;  
   private String sex;  
 }  

Instead of initializing the List as normal ArrayList, use the LazyList class (Commons-collection package). This is to avoid "IndexOutOfBoundException" when the data is submitted back to the controller.

2) JSP

<form:form action="${formAction}" name="childrenDetails">  
 <table><thead>  
  <tr>  
   <th><b>Name</b></th>  
   <th><b>Sex</b></th>  
   <th><b>Age</b></th>  
  </tr>  
  </thead>   
  <tbody>   
   <c:foreach items="${command.children}" var="child" varStatus="loop">   
    <tr>   
     <td><c:out value="${child.name}"></c:out>  
       <form:hidden path="children[${loop.index}].name"/>   
     </td>  
     <td><form:select path="children[${loop.index}].sex">  
        <option value="">--Please Select--</option>  
        <option value="M">Male</option>  
        <option value="F">Female</option>   
       </form:select>  
     </td>  
     <td><form:input path="children[${loop.index}].age"/></td>   
    </tr>  
   </c:foreach>   
  </tbody>   
  </table>  
 </form:form>  

This code will print all the children names and show input fields for Age and Sex field.

3) Validator Class

When the List data is submitted back, Spring constructs the data back to the Form bean with help of the LazyList collection. Validation and raising error on these fields is very simple.

 public class MotherBeanValidator implements org.springframework.validation.Validator {  
 .......   
   public void validate(Object target, Errors errors) {  
     MotherFormBean bean = (MotherFormBean) target;  
     List<Child> children = bean.getChildren();  
     for (int i = 0; i < children.size(); i++) {  
       if (StringUtils.isEmpty(children.get(i).getSex()))   
        errors.rejectValue("children[" + i + "].sex", "select.sex");  
       if (StringUtils.isEmpty(children.get(i).getAge()))   
        errors.rejectValue("children[" + i + "].age", "select.age");  
     }  
   }  
 }  

JSP Changes:
Include <form:errors> tag after the sex and age input fields.
eg.)
 <form:errors path="children[${loop.index}].sex"/>   
Hope this helps you in displaying the Collection data using Spring MVC and validating it.

Thanks to Matt Flemings blog for shedding some light on the LazyList.