The other day I re-encountered an interesting problem involving submitting dynamic lists of data and on a form and binding that data back to lists in a Spring MVC form-backing object. The problem I had occurs (typically in my experience) when you have a form where you are using javascript to dynamically create x number of new form inputs, which need to be bound back to a list of elements on a form backing object. To illustrate I’ll use a simple example of a form that displays a shopping basket and allows users to edit existing and add more basket items using javascript. The form backing object for this might appear as:

public class ShoppingBasketForm
{
    private List<ShoppingBasketItem> items;
}

The desired behaviour of the form is that the user should be able to add any number of new ShoppingBasketItems and bind them back to the List on the form-backing object, using javascript to dynamically create the form inputs for each ShoppingBasketItem’s properties. The problem with this approach is that as you create each new ShoppingBasketItem you have to set in each input a reference to the position in the items List that you want to bind back to i.e. the first ShoppingBasketItem you create needs to bind to command.items[0], the second to command.items[1] etc (where command is the name of the form-backing object).

However if you were to try and implement this you would find that as soon as you added new basket items and submitted the form you would encounter an ArrayIndexOutOfBoundsException. This is because any new items are being bound back to elements in the items List that are null.

There are several techniques you could use to solve this problem. One possible approach would be to add a number of ‘empty’ elements to the items List when the form-backing object is first initialised. This would ensure that the new items being added would bind back correctly. However, the obvious problem with this technique is that on a dynamic form, such as that which we describe above, you never know exactly how many items may be added by any given user and thus how many ‘empty’ elements would need to be provided initially in the items list.

A much more elegant solution to this problem is to use Spring’s AutoPopulatingList. Looking at the Spring API shows that this class ‘allows for elements to be automatically populated as they are requested. This is particularly useful for data binding to Lists, allowing for elements to be created and added to the List in a “just in time” fashion’. In the example above we can use this by changing the list of ShoppingBasketItems reference on the form-backing object to be an AutoPopulatingList:

public class ShoppingBasketForm
{
    private AutoPopulatingList items;
}

We then change the formBackingObject method in the controller to instantiate a new AutoPopulatingList:

ShoppingBasketForm sbf = new ShoppingBasketForm();
sbf.setItems(new AutoPopulatingList(ShoppingBasketItem.class));

With this configured we can use javascript on the page to add as many new input elements that need to map back to the ShoppingBasketItem list. When the form is submitted, for each newly referenced basket item that does not already exist in the list the AutoPopulatingList will instantiate a new ShoppingBasketItem and then bind any form variables back to items in the list.

Sometimes instantiating just a single class in the list is not enough. For example, you might want to create a ShoppingBasketItem and also a number of classes that are properties of ShoppingBasketItem. To do this we can create a new class which extends the AutoPopulatingList.ElementFactory interface. Doing this allows you to define what objects to be instantiated when the ShoppingBasketItem is first created when the form is submitted. For example:

public class ShoppingBasketItemElementFactory implements ElementFactory
{
@Override
public ShoppingBasketItem createElement(int index) throws ElementInstantiationException
{
ShoppingBasketItem item = new ShoppingBasketItem();

// set some default properties on the item
item.setItemNo(0);
item.setItemName("");
item.setQty(1);

// instantiate any other properties on the item</code>

BasketItemStatus status = new BasketItemStatus();
status.setId(1);
return item;
}
}

In this example we use the ElementFactory to instantiate the ShoppingBasketItem and a number of it’s properties which we set to default values. Once each item is instantiated and put into the list then we can bind back to the properties and override them with our form values. To set up the AutoPopulatingList in this manner we can configure it in the formBackingObject method like this:

ShoppingBasketForm sbf = new ShoppingBasketForm();
sbf.setItems(new AutoPopulatingList(new ShoppingBasketItemElementFactory()));
We can then bind to properties in the list by doing the following:
Bind to an input to the itemNo property on the first ShoppingBasketItem to be added to the list:
#springBind("command.items[0].itemNo")
<input type="text" name="$status.expression"/>
Bind an input to the id property on the BasketItemStatus property on the second ShoppingBasketItem in the list:
#springBind("command.items[1].status.id")
<input type="text" name="$status.expression"/>