Spring Forms, Dynamic Lists and Ajax

Spring Forms and Dynamic Binding Lists

Introduction

After my recent posts on Spring forms and annotations – dynamic lists is logically another step to be discussed.

In the previous examples we use a form backing object – the form backing objects have tended to be very basic and primitive

Using dynamic lists within these objects makes them slightly more complicated. Suppose the following scenario:

Class enrolment example

  • User chooses to create a new class
  • User types in class name
  • User also has to specify one (or many) names of students in the class

Using that scenario you can imagine a form in which there is a class name field and then below that a simple student name field. When the user clicks add next to the student name – javascript should simply show another input field for a student name allowing a user to add one or more students.

Eventually when the form gets submitted to the server a list of students will be populated.

However if we don’t using Spring’s dynamic lists – specifically Spring’s AutoPopulatingList – we cannot perform such an action as we have out of bounds errors.

Configuring your build environment

Your workspace needs setting up as the previous examples. Using Eclipse create a new project. For your src directory enter the value /WEB-INF/src. For your output directory use the WEB-INF/classes directory.

Add a new lib directory in your WEB-INF folder. Under your WEB-INF directoy you will need the following JAR files (all of which can be obtained from the Spring download). Add the jars to your buildpath.

commons-beanutils.jar
commons-collections.jar
jstl.jar
spring-core.jar
spring-webmvc.jar
spring.jar
standard.jar
tiles-api-2.0.6.jar
tiles-core-2.0.6.jar
tiles-jsp-2.0.6.jar

Next add the Spring servlet.xml to your application. Create a file called dynamiclists-servlet.xml in your WEB-INF folder. Add the following to the file:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context 
                           http://www.springframework.org/schema/context/spring-context.xsd">
 
 
 
    <context:component-scan base-package="com.annotations.controller" />
    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
 
    <!-- Enables plain Controllers -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
 
    <bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
        <property name="definitions">
	    <list>
		<value>/WEB-INF/tiles-defs/templates.xml</value>
	    </list>
	</property>
    </bean>
 
    <bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
	<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
    </bean>                
</beans>

Next add your web.xml in the same location as your Spring servlet.xml file. Add the following contents:

<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app version="2.4"
		 xmlns="http://java.sun.com/xml/ns/j2ee"
		 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		 xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
 
	<display-name>Spring Dynamic Lists Example</display-name>
 
 
	<servlet>
		<servlet-name>dynamiclists</servlet-name>
        	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        	<load-on-startup>1</load-on-startup>
	</servlet>
 
	<servlet-mapping>
    		<servlet-name>dynamiclists</servlet-name>
    		<url-pattern>*.page</url-pattern>
    	</servlet-mapping>
 
 
</web-app>

Next add a new folder in your WEB-INF directory called tiles-defs and add a file called templates.xml within that. Add the following to the templates.xml file

<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE tiles-definitions PUBLIC
       "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
       "http://tiles.apache.org/dtds/tiles-config_2_0.dtd">
 
<tiles-definitions>
 
	<definition name="form.view" template="/pages/form.jsp">
	</definition>
 
	<definition name="formStudentInsert.view" template="/pages/formStudentInsert.jsp">
	</definition>
 
	<definition name="formResult.view" template="/pages/formResult.jsp">
	</definition>
 
</tiles-definitions>

That should be the workspace setup – next to create our form JSP pages

Form JSP pages and a touch of jQuery

Firstly create a new folder under your application called js. Next download jQuery and place the jQuery download within the folder.

Under your project create a new folder names pages and create a file named form.jsp within that.This will be our class creation form.

Add the following to your form.jsp file

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
 
<script type="text/javascript" src="/dynamiclists/js/jquery-1.3.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
	var studentPosition = 0;
	$("#addStudentButton").click(function() {
		studentPosition++;
 
		$.get("<%=request.getContextPath()%>/appendStudentView.page", { fieldId: studentPosition},
			function(data){
				$("#submitRow").before(data);
		});
	});
});
</script>
 
<form:form method="post" name="classForm" id="classForm" commandName="classCommand">
	<table>
		<tr>
			<td>Class Name:</td>
			<td colspan="2"><form:input path="className" size="40" /></td>
		</tr>
		<tr>
			<td colspan="3"><strong>Students</strong></td>
		</tr>
		<tr>
			<td>Student name</td>
			<td>
				<spring:bind path="classCommand.students[0].studentName">
					<form:input path="${status.expression}" size="40" />
	  			</spring:bind>
			</td>
			<td><input type="button" id="addStudentButton" value="Add" /></td>
		</tr>
		<tr id="submitRow">
			<td>&nbsp;</td>
			<td><input type="submit" value="Save Class" /></td>
		</tr>
	</table>
</form:form>

This probably requires a bit of an explanation. The first two lines of the file include the required Spring tag libraries.

After that we include the link to our downloaded jQuery Javascript source. I’ll make an assumption that people are familiar with jQuery.

Next we use jQuery to add a click event listener to our form ‘Add’ button. When the user clicks add button we fire off an Ajax Get request to our server which sends an incremented number as part of its data string. This Ajax request hits our server which simply appends a new row to the form for another student name. We use the number for the index number in the collection of students.

The response is simply appended as a new row in our form before the form submit button. After that Javascript the rest is a basic Spring form.

Next create a new file called formStudentInsert.jsp in the same location as your form.jsp and add the following contents.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
 
<tr>
	<td>&nbsp;</td>
	<td>
		<form:input path="classCommand.students[${studentNumber}].studentName" size="40" />
	</td>
	<td>&nbsp;</td>
</tr>

As mentioned before this is JSP that gets appended to our form when we send the Ajax request.

The form should look similar to the image below.

Picture of class form

Next create a JSP page that is displayed when the user submits the form. Create a new JSP called formResult.jsp and add the following contents.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
 
<h2>Class ${savedClass.className}</h2>
 
<p>Students</p>
 
<ul>
	<c:forEach var="student" items="${savedClass.students}">
		<li>${student.studentName}</li>
	</c:forEach>
</ul>

The Form Backing Object

We next need to create the form backing objects that will be used for this example. Create a new Java class called Student and add the following contents:

/**
 * @author Eggsy - eggsy _at_ eggsylife.co.uk
 *
 */
public class Student{
	private String studentName = null;
 
	public String getStudentName() {
		return studentName;
	}
 
	public void setStudentName(String studentName) {
		this.studentName = studentName;
	}
 
}

This class is a simple Student object. Student objects will belong to a class object. Now we can go ahead and create the Class object.

Create a new class called ClassCommand and add the following:

import org.springframework.util.AutoPopulatingList;
 
/**
 * @author Eggsy - eggsy _at_ eggsylife.co.uk
 *
 */
public class ClassCommand {
	private String className = null;
	private AutoPopulatingList students = null;
 
 
	public String getClassName() {
		return className;
	}
 
	public void setClassName(String className) {
		this.className = className;
	}
 
	public AutoPopulatingList getStudents() {
		return students;
	}
 
	public void setStudents(AutoPopulatingList students) {
		this.students = students;
	}
}

The class command object is the backing object we shall use for our form. It simply has a class name. However it does also have a list of students. For this we could have used a basic java.util.ArrayList but this causes issues when attempting to bind the form. As the list wouldn’t (initially) have any contents we would experience IndexOutOfBounds exception messages. This is where we can use the incredibly helpful Spring AutopopulatingList.

Spring Annotated Form Controller

Finally we need to create the Spring Form controller that handles our various requests. Note in the servlet XML file we specified that we would scan the package com.annotations.controller package for controller annotations. Go ahead and create this package. (You can of course change this). Now create a class called ClassFormController containing the following:

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.AutoPopulatingList;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
 
import com.annotations.command.ClassCommand;
import com.annotations.command.Student;
 
 
/**
 * @author Eggsy - eggsy _at_ eggsylife.co.uk
 */
@Controller
public class ClassFormController
{
 
	@ModelAttribute("classCommand")
    	public ClassCommand getClassCommand()
    	{
		ClassCommand classCommand = new ClassCommand();
		classCommand.setStudents(new AutoPopulatingList(Student.class));
		return classCommand;
    	}
 
	/**
	 * <p>Called when the form is initially displayed</p>
	 * @param model
	 * @return
	 */
	@RequestMapping(method = RequestMethod.GET, value="/processClassForm.page")
	protected String showNameForm(ModelMap model)
	{	
		return "form.view";
	}
 
	/**
	 * <p>Called when the user clicks the add button</p>
	 * @param fieldId
	 * @param model
	 * @return
	 */
	@RequestMapping(method = RequestMethod.GET, value="/appendStudentView.page")
	protected String appendStudentField(@RequestParam Integer fieldId, ModelMap model)
	{	
		model.addAttribute("studentNumber", fieldId);
		return "formStudentInsert.view";
	}
 
	/**
	 * <p>Called when the user finally submits the class form</p>
	 * @param classCommand
	 * @param model
	 * @return
	 */
	@RequestMapping(method = RequestMethod.POST, value="/processClassForm.page")
	protected String onSubmit( @ModelAttribute("classCommand") ClassCommand classCommand,  ModelMap model )
	{
		model.addAttribute("savedClass", classCommand);
		return "formResult.view";
	}
 
}

I’ll assume you’re comfortable with Spring annotations so I’ll just highlight the important parts. Firstly notice the way we declare our ClassCommand object. We new up the object then set the Students list as a new AutoPopulatingList of type Student.class. This is again so that we can ensure we experience no IndexOutOfBounds errors. The next method is called when we display our form simply returning the form.view. After that we have the method that gets called when the user clicks the Add button. This returns the small HTMl snippet that gets appended to our form. Finally we have the onSubmit method that gets called when we submit our form and returns the formResult view.

Ok so thats pretty much it. If you followed the examples word for word you after setting up your web application you should be able to visit http://localhost:8080/dynamiclists/processClassForm.page and test your web app.

You can also download the example project here within the project there is a docs folder that also includes a sample Tomcat context file for application deployment.

References: http://blog.richardadamdean.com/?page_id=119

Any comments or questions are always welcome!

28 thoughts on “Spring Forms, Dynamic Lists and Ajax

  1. Hi,

    I’m using your example in a project but i’m not using jquery’s get method to inject a new page, instead I’m using the append method to append my input elements.

    My problem is when I submit the append field data is not in my AutoPopulatedList. Is the function(data) function necessary? I’m not sure what that function does. Also, when I implement it my append does not work.

    Thaks for any help

  2. Thanks for such a good article. It saved lot of my time.
    I have a similar requirement, but the Student class contains a hibernate association: “private Set hierarchy”
    I am not sure how to reference a hierarchy fields in JSP and ClassCommand. Can you please give some suggestion.

  3. Thanks for such a good article. It saved lot of my time.
    But I have small problem when I try to validate the attributes of student using Spring Validator. Can you please help me in that regard.

  4. can some post a war file of this project… I am trying to do something similar to this post..this will help me a lot..if I can run and see how exactly it works..I was not able to run the downloaded zip file..it gives me no error but, still Its not running..

  5. The input fields are appended via jQuery to the form so the DOM is being changed.

    Of course if you wanted the fields to persist then you would have to persist the class changes and rebuild the JSP on load.

  6. This article realyl helped. Thanks a ton. Although on page refresh the input fields are gone, is there any way we can retain the added input text boxes on page refresh.

  7. can some one put a war file out here so I can just import it. I can’t get the project to run when I import it in to a new workspace. The project does not know it is a web based project.

  8. Very helpful post.

    I was able to add new rows without need of an aditional JSP nor a dedicated controller. Thanks to JSTL and javascript is easy to create a row template and replace on client side a placeholder with the row number.

    However I thing my implementation could be improved, is there any way to retrieve jsp tag body without have been processed?

  9. Hi.

    In this example you have a students class which has student name property.

    We have a similar requirement. Here we have a dropdown which contains 4 different types of auctions. Every auction has a different set of parameters.

    If I select any auction the corresponding auction parameters should get displayed.

    Since every auction has different parameters like start date, end date and so on, how should I use spring bind tag to bind those parameters?

    I have an auction class as my command class and it contaims AuctionParamValues as list.
    The auctionParamValues is an object that contains paramname and param value.

    Could you please help me with this one?

  10. This is a tremendous help. It solves a problem that I have encountered with a new project. I just want to say thank you for such a thorough post — it will be a big time saver for me.

    Cheers!

  11. Hi there,

    I guess in the controller submit method you could add the BindingResult parameter and implement your own validator.

    You could also use the new JSR-303 Bean Annotation validation on each individual student and that should vailidate so the onSubmit method would become:

    @RequestMapping(method = RequestMethod.POST, value="/processClassForm.page")
    protected String onSubmit( @ModelAttribute("classCommand") @Valid ClassCommand classCommand,  ModelMap model )
    {
    	model.addAttribute("savedClass", classCommand);
    	return "formResult.view";
    }
  12. Hi there,

    In that case, rather than using the @ModelAttribute function you could just populate the required object (by placing into the ModelMap) in the GET annotated controller method in that case the object would be populated when and only when the form is displayed.

    If you need to pre-populate the Class Command in this example such as calling into a database as you mention you can do so by all means. The AutoPopulating list of the class command can be updated in the same way as a java.util.List

  13. I wrote a controller the same way you did (using annotations) as I wasn’t able to translate it to non annotated code.

    I see one issue with your code and that is that getClassCommand will be called every time @ModelAttribute(“classCommand”) is injected.

    What about if you need your ClassCommand instance to be “kept alive” between the calls?
    That would be necessary if you did something else than just “ClassCommand classCommand = new ClassCommand();” in you getClassCommand() method. Loading a ClassCommand from the database for instance.

  14. “admin”
    The problem with just inherit SimpleFormController and override the two methods you mentioned is that SimpleFormController is not a MultiActionController which would be required to add appendStudentField for instance.

  15. Hi there,

    I have tested the download and everything seems fine.

    After downloading and extracting the Zip file. In Eclipse Click File then Click Import and choose ‘Existing projects into workspace…’ then on the next screen choose the downloaded folder.

    After importing everything should build OK?

  16. Hi there

    I’m glad it helped you.

    In terms of doing it with a non annotated controller – I haven’t done it a while – but I think you should just be able to extend the Spring ‘SimpleFormController’ object then override the onSubmit and formBackingObject methods. The formBackingObject method would be where you return the ClassCommand object as dictated by the tutorial.

    I hope this helps,

    Please let me know if you need any further advice

  17. This code is almost exactly was I was looking for except I’m not a big fan of annotating my controllers. I.e I prefer the xml configuration. I’m not very familiar with Springs annotations and find it difficult to “translate” your annotations to xml. Could you give me some hints?

  18. Any way this could be done with an app that is already using the non-annotation-based spring mvc? I’m trying to create a second controller extending AbstractController and return the html, and I have that done with the jQuery etc… The problem is trying to bind to the command with form:input coughs because the command initiated by the original controller isn’t available to the 2nd controller. Thoughts? Thanks a lot, this is great so far!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>