It looks like trying to use Struts’ nested indexed properties in a form with table-layout, with every line (a few formfields) as an editable separate entity, has driven a lot of people (including me) nuts.
With nothing about it in the manual, and no answers to be found on the web, here’s finally a solution.

The JSP:

    <logic:iterate name="orderForm" property="orders" id="rememberMe">
        <html:text name="rememberMe" property="fieldA" indexed="true"/>
    </logic:iterate>

will create a list in HTML like

    <input type="text" name="rememberMe[ 0 ].fieldA">
    <input type="text" name="rememberMe[ 1 ].fieldA">

([0] and [1] being the index, which is added automatically (the ‘indexed=”true”‘ attribute)

The formbean of course needs the collection (assuming there is a Line class/mini-formbean, having fieldA as membervariable with a public getter):

    private List orderLines;
    public List getOrderLines() { return orderLines; }
    public void setOrderLines(List orderLines) { this.orderLines = orderLines; }

but also, here is the trick, a method with a special name that returns an indexed
element (orderline) from the collection of orders:

    public Line getRememberMe(int index) { return (Line)orderLines.get(index); }

As you see, the name of the method needs to correspond with the ‘id’ attribute in the
logic:iterate tag and the ‘name’ attribute in the html:text tag.

This looks like it’s going to work smoothly, but there’s still one problem left.
Everytime a client posts a new request he gets a new (resetted) formbean, so the
collection is not initialized at the moment the form-values will be copied into the bean.
Normally this is ok, but since Struts (or commons.BeanUtils, actually) calls the
“getRememberMe” method to get an element from the collection to set a value in one of
its fields, this can result in a NullPointerException or ArrayIndexOutOfBoundsException.

There are of course multiple solutions for this, among which:

  • If you know the maximum amount of orderlines you will have in your form, you could pre-construct
    them all in the constructor of the formbean, using default values.
  • An automatically growing collection that creates, initializes and adds a new element to
    the collection if the getter is called. This might feel weird at first, but i do think
    it is the best solution. It has the additional plus that a dynamically growing form
    (for example by using the javascript/DOM cloneNode method) will always fit in the collection.

This class could look something like this:

public class AutoGrowingList extends ArrayList
{
    private Class clazz;

    public AutoGrowingList(Class clazz)
    {
        super();
        this.clazz = clazz;
    }

    public Object get(int index)
    {
        Object obj = super.get(index);
        if (obj == null)
        {
            obj = clazz.newInstance();
            super.add(obj);
        }
        return obj;
    }
}