Skip navigation.
Home

Dynamic list binding in Spring MVC

The Spring MVC documentation just isn't quite there. It is pretty basic, and doesn't really help with some common medium difficulty scenarios. The one I am documenting today is how to take a typical model (with lists of dependent objects), show it in a form, and get the graph back upon submission...

Rather than go through the entire thought process from beginning to end, I am going to show the end state and then explain the major glue points that make everything work. The assumption is that you have an ok understanding of Spring MVC.

The model

Here's a simple model for the illustration. There is an object named Grid which has a list of Block objects..


public class Grid {
  private List blocks = 
    LazyList.decorate(
      new ArrayList(),
      FactoryUtils.instantiateFactory(Block.class));

  public List getBlocks() {
    return blocks;
  }

  public void setBlocks(List list) {
    blocks = list;
  }
}
  • The LazyList class is the key here. This is in from the commons-collections package. I'll talk about why this is key later.

public class Block {
  private String id, description;

  public String getDescription() {
    return description;
  }

  public String getId() {
    return id;
  }

  public void setDescription(String string) {
    description = string;
  }

  public void setId(String string) {
    id = string;
  }
}
  • nothing special here.. just a bean.

JSP


<c:forEach items="${grid.blocks}" varStatus="gridRow">
<spring:bind path="grid.blocks[${gridRow.index}].id">
  <c:out value="${status.value}"/>
  <input type="hidden" name="<c:out value="${status.expression}"/>"
    id="<c:out value="${status.expression}"/>"
    value="<c:out value="${status.value}"/>" />
  </spring:bind>
<spring:bind path="grid.blocks[${gridRow.index}].description">
  <c:out value="${status.value}"/>
  <input type="hidden" name="<c:out value="${status.expression}"/>"
    id="<c:out value="${status.expression}"/>"
    value="<c:out value="${status.value}"/>" />
</spring:bind>
</c:forEach>
  • This snippet loops over the blocks attached to the grid. The gridRow (varStatus) is an instance of the LoopTagStatus class which can give me the index of the current row in the list of blocks.
  • The bind path takes this index into account and will generate hidden input tags named like: grid.blocks[0].id, grid.blocks[1].id, etc. This is how Spring MVC can automagically know which item in the list should be used.

Form controller


public class ForecastAllocationGridFormController 
  extends SimpleFormController {

  public ForecastAllocationGridFormController() {
    setCommandName("grid");
    setCommandClass(Grid.class);
  }

  protected Object formBackingObject(HttpServletRequest request)
    throws Exception {
    Grid grid = new Grid();
    if (!isFormSubmission(request)) {
      grid = CreateGrid.expensiveToCall();
    }
    return grid;
  }

  public ModelAndView onSubmit(
    HttpServletRequest request,
    HttpServletResponse response,
    Object command,
    BindException errors)
    throws Exception {

    Grid grid = (Grid) command;
    //hey it's fully populated

    return new ModelAndView(getSuccessView());
  }
}
  • I would have loved to find a good sequence diagram for the various controllers but I couldn't find any.
  • Here's the full flow: 1.Request page --> 2.formBackingObject() --> 3.show jsp --> 4.Form submit --> 5.formBackingObject() --> 6.bind (overlay form values onto object graph) --> 7.onSubmit().

The problems

Here are the various problems that I had:

  1. How could I submit a dynamically created list? Answer, is in the spring:bind of the JSP. Once you properly index the form elements, spring takes care of the rest.
  2. If it is really expensive to create the object graph (#2 in the flow), how can I not take that hit twice? Answer, the LazyList. If the list were not lazy (just a regular ArrayList()), #6 would fail with an InvalidPropertyException: Invalid property 'blocks[1].id' of bean class [Grid]: Index of out of bounds in property path 'blocks[1]'; nested exception is java.lang.IndexOutOfBoundsException: Index: 1, Size: 0. This is because Spring is attempting to do a blocks.get(1) on an empty list.
  3. Of course you could remove the isFormSubmission() check and just load the object graph (via formBackingObject()) again and you would avoid the IndexOutOfBoundsException.. but what happens if the graph comes back differently on a subsequent call or just takes a while?

I'm having problems

I'm having problems dynamically adding rows via JavaScript and gett Spring to handle it correctly. I can't tell if your solution solves this problem. Based on what you have listed under Problem 1, it sounds like it might, but I can't tell. If this does solve that particular problem, did you have to do anything more than simply add the row via JavaScript? I've seen some references to registering custom binders, but I've tried that as well and it doesn't seem to work.

LazyList Worked

Using the commons collections LazyList worked, it got rid of the ArrayIndexOutOfBoundsException. However, I don't understand why the added element isn't there when binding, but it is there by the time it gets to the onSubmit() method of the Controller.

Matt Fleming's picture

Light documentation

I don't know if you've read this in-depth presentation on Spring MVC (pdf) but it will probably clear some things up for you. I know it helped me get a handle on the sequence of things. In the absence of a not-yet-found-by-me controller sequence diagram, it is the next best thing.

As far as your question, my guess is that what we are calling bind() happens before overlayInputValues(). If you register a Validator for the form... you will see all of the javascript created form elements present in the validate(Object arg0, Errors errors) method and all subsequent methods (e.g. onSubmit()).

-Matt

Trying to figure it out

I am pretty new to doing forms dynamically in HTML and with Spring. I am not clear where you put the "real" unhidden text fields for the table -- where you would enter the data values. Is there a correlated JavaScript that goes along with this example. A full example would go a long way to help my understanding of the topic.

I guess what I'm not clear on is why the input type is "hidden" in the .jsp snippet, and how real "text" input types get bound to these "hidden" types that get bound by spring to the BindStatus object.

I understand the concept of indexing the elements through c:forEach, so they can be picked up and bound by Spring, just not sure where the rubber meets the road on this one.

Any help anyone can provide would be great! I'm perplexed.

Matt Fleming's picture

RE: Trying to figure it out

I just used hidden fields (in the example) but the type of field doesn't really matter. You could use text fields or radio buttons or whatever. The binding happens exactly the same regardless of the type of input field. Just surround the input with bind and you are all set.

Can you please post the complete example, Matt?

Hi,

I am new in the Spring Framework and I tried very hard to find a dynamic forms example. This is the only one I am able to google. The example as it is already answered some questions for me. But I still have a couple of questions. For example, why did you not have a showForm() method? Did you not need one, or did you just not post it because you thought it is trivial? Also, I'd be very interested in seeing how you wire the beans in your servlet.xml. I wonder if you can post the entire source code so I can compile and play with the deployment. I'd greatly appreciate it if you can do so.

Again, I am new here and I care more about how to make dynamic forms work as opposed to how to make them work efficiently, which seems to be what you focused on in this example. It will help me greatly as well if you could point me to a complete example of dynamic forms.

Thanks for your help in advance!

-lpanl

Matt Fleming's picture

Re: example

Quote:
For example, why did you not have a showForm() method?
I'm extending SimpleFormController so it really isn't necessary.

I don't wire beans in servlet.xml.. I usually use many separate files. Since you are just starting out with this stack, I'd recommend using appfuse. All of the basic kinds of annoyances that happen with starting up are taken care of.. After you get appfuse up.. try to make a dynamic form again.

-Matt

Binding to dynamic checkboxes does not work for me

&Hi Matt:

Thanks a lot for your response!

Now I've got the bean wiring part working. But the binding part does not. status.value is never true in the JSP, even when fieldType.NASAapproved (boolean, see below) is true in the database.


<c:forEach var="fieldType" items="${model.Catalog.fields}" varStatus="fieldRow">
<tr>
<td align="center">
  <c:out value="${fieldType.cycle}"/>
</td>
<td align="center">
  <c:out value="${fieldType.version}"/>
</td>
<td align="center">
  <c:out value="${fieldType.dataAuthor}"/>
</td>

<td align="center">
  <spring:bind path="model.Catalog.fields[${fieldRow.index}].NASAapproved">
  <input type="hidden" name="_<c:out value="${status.expression};/>">
  <input type="checkbox" name="<c:out value="${status.expression}"/>" value="true"
     <c:if test="${status.value}">checked</c:if>/>
  </spring:bind>
</td>
<td align="center">
  <spring:bind path="model.Catalog.fields[${fieldRow.index}].CNESapproved">
  <input type=checkbox name="cnesckbx"
  <c:if test='${fieldType.CNESapproved == true}'> CHECKED</c:if>
  > Approved
  </spring:bind>
</td>
<td align="center">
  <c:out value="${fieldType.GDRreleaseDate}"/>
</td>
<td align="center">
  <spring:bind path="model.Catalog.fields[${fieldRow.index}].SGDRstaged">
  <input type=checkbox name="stgckbx"
  <c:if test='${fieldType.SGDRstaged == true}'> CHECKED</c:if>
  > Staged
  </spring:bind>
</td>
<td align="center">
  <c:out value="${fieldType.SGDRreleaseDate}"/>
</td>
<td align="center">
  <c:out value="${fieldType.emailId}"/>
</td>
</c:forEach>
</tr>

</table>

<br>
<input type="submit" value=" Submit ">

</form>

Do I have to use the LazyList to get things done correctly? I have a Catalog,which holds an ArrayList of Fields, which is a collection of variables, boolean NASAapproved being one of them. In the above jsp, if I replace status.value by fieldType.NASAapproved, the checkboxes get checked correctly (i.e., consistent to the DB). When I submit the form with all checkboxes checked, the var NASAapproved is false in the call to Fields.getNASAapproved(). This seems to mean that binding did not happen.

It seems that both dynamic forms and checkbox binding are hard problems because many people asked the same questions. But I could not find a solution that has both and is complete. I borrowed some ideas from: http://opensource.atlassian.com/confluence/spring/display/DISC/Working+with+Checkboxes

I'd really appreciate it if you can provide more help on this one.

-lpanl

Matt Fleming's picture

Re: binding does not work for me

Whenever I'm doing checkboxes I start with simply printing out the value. Then I move into the jstl and getting the checkbox defaults to work properly. So you might want to simply print out the value as text just to make sure that you are actually getting the value you want. In fact, print out both the ${status.expression} and ${status.value} before trying to hide them behind form elements.

If the data is truly being populated correctly, I think your issue is in the jstl test... I've had some problems testing booleans in the past. I'm pretty sure that the second way that you do your test will work like you want it to.. So instead of:


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

write


<c:if test="${status.value == true}">

-Matt

p.s. the html input format for code isn't working properly.. you should be allowed to use code tags to show escaped html but it isn't working properly.

looks like something basic is wrong with my code


  <spring:bind path="model.Catalog.fields[${fieldRow.index}].NASAapproved">
  <c:out value="${status.expression}"/>
  <c:out value="${status.value}"/>
  <c:out value="${fieldType.NASAapproved}"/>
  <input type="hidden" name="_<c:out value="${status.expression}"/>">
  <input type="checkbox" name="<c:out value="${status.expression}"/>" value="true"
     <c:if test="${status.value == false}">checked</c:if>/>
  </spring:bind>

Hi Matt:

It seems that something fundamental is wrong with my code. I added three "print" statements (shown above) and the printouts of status.expression and status.value came out to be blank. But the printout of fieldType.NASAapproved was good, indicating the the model data from my showForm is returned to jsp correctly.

Then I tried your way of jstl test (see above code) and it is apparent that status.value is not initialized with any value at all; even testing against false does not check any checkbox.

Another issue is with my formBackingObject. It seems that I need to allocate memory for the List in my Catalog, or else I will get a NullPointer when I push Submit. Is this normal or did I do something wrong and not utilizing Spring's memory allocation?

Thanks a lot!

-lpanl

Matt Fleming's picture

break it down more

You seem to have bitten off more than you can chew.. Eye-wink The key to figuring out what is going on is isolation. You have to make sure that you are 100% certain of the layer below the one you are about to test. It sounds like you are having trouble with multiple layers.. the controller and the display.

For dynamic binding you have to do two simple things:

  1. make sure that all collections are lazily instantiated. (this is your NPE when you push submit problem).
  2. Submit an html form the proper way.

Have you tried to create a hardcoded html form that declares the variables properly? Instead of dynamically creating the form, just create some static html and make sure it submits properly.

So instead of model.Catalog.fields[${fieldRow.index}].NASAapproved use an input field named model.Catalog.fields[0].NASAapproved I'd create like three fake rows and make sure that I understood how Spring goes from web form to object. I wouldn't even try to use data from the formBackingObject.

Once you do that, add one level of dynamism (if that is even a word) until the whole thing comes together.. I feel like you jumped right to the end without a very deep understanding of how any of the stuff works.

-Matt

showForm() not needed?

>> I'm extending SimpleFormController so it really isn't necessary.

Hi Matt:

I am using SimpleFormController too, but if I
comment out my showForm(), the jsp will not
get the command object (or an empty one)
resulting no rows in the table.

I wonder how you could get away without showForm().
Sorry I am new to jsp/Spring ...

Thanks,

-lpanl

Matt Fleming's picture

See above

Same as I said above.. My main complaint with all web frameworks is that they almost encourage people to not understand the basics and jump into a complex scenario. This leaves the novice rimrocked.

-Matt

When is isFormSubmission(request) ever true?

Hi Matt:

Thanks for your advice! I have gone through the path of making
it very simple to find out a couple of bugs and then coming
back to the full functionality using dynamic forms and checkboxes
and luckily I now made it work.

One question I have now is that I observed that the
test if (!isFormSubmission(request)) is always true
whether I direct the successView to be a new htm
or to come back to the same view. As a result,
I did not need to use the LazyList and every
single formBackingObject call is going to hit
the DB and generate the catalog (expensive to call).

So my question is: in what situation the test
isFormSubmission(request) is ever true?
My code is listed below.

Thanks a lot!

-lpanl

protected Object formBackingObject(HttpServletRequest request)
throws ServletException {

Catalog catalog = null;

logger.info("CCCC===== StreamliningController.formBackingObject called !");

if (!isFormSubmission(request)) {
catalog = cm.getCatalog();
}
else
logger.info("^^^^^^^^^^^^ StreamliningController isFormSubmission!");

return catalog;
}

Matt Fleming's picture

RE: When is isFormSubmission(request) ever true?

It will be true when you submit an html form on the jsp to the controller.

-Matt

How to tackle polymorphism in LazyList

Hi,
Although the LazyList works for me, I do not know why. I am using the new spring:form tags and the form submission worked well for me with a lazylist. The problem I am having now is since the object instantiation is in the hands of the framework when we submit the form, I have to specify a concrete class name in the FactoryUtils.instantiateFactory() method. How do I get around this problem if I have an abstract class and on runtime I need to pass an inherited class object to the instantiateFactory method. So I have :

private List baseList = LazyList.decorate(new ArrayList(), FactoryUtils.instantiateFactory(Inherited1.class));

So now I want to be able to decide which class to pass Inherited1.class or Inherited2.class. How and on which callback method can I do that?

I had to log in to post a comment? Is this 1999?

Is this the sequence diagram, although this is a not at all sequence diagram, that you were looking for??

http://mikenereson.blogspot.com/2007/02/spring-mvc-controller-processing.html

Matt Fleming's picture

Re: sequence diagram

That's a pretty good resource. It definitely would have answered my initial question about the sequence which methods were called..

Spring MVC Sequence -Matt

Dynamic List Binding and coming from error

I used a similar method for your list binding.
Here is my question.
I have a list in my command bean.
When I click add phone on my page I dynamically generate the input for the phone number. I can add as many as I want.
When I submit Spring binds it to the collection like you have. Even if I go into an error and the form is invalid the phone numbers I have come back populated. The issue I have is if I click a javascript link to remove the input field and then go onsubmit it does not "rebind" what is visible since what I have in the list is still there. I am doing this as I sit on the form with errors. Say I fogot to input a name. Well if at that point I decide to remove a phone and input a name that form then passes validation but it still binds two phone numbers not one.

I tried clearing the list on the init binder but that did not work.

I am trying to figure a way to dynamically through javascript be able to remove the input fields and then have the form rebind the visible ones.

The issues with the removing is only happening when I come back from a error onvalidating.

Ideas..
Thanks.

Matt Fleming's picture

Re: Dynamic List Binding and coming from error

Hopefully the answer to this question can be answered by this other comment. I'll be writing a couple of more articles on this ever expansive topic. Eye-wink

-Matt

Great I am looking forward

Great I am looking forward to solutions. It just seems so trivial but it is complex. It stinks to have a massive form that has all the fields in the command object but then you have a set of phone numbers. It would be nice to handle this in that if you remove/add and then on final binding it can be handled. Not having to use Ajax just for the phone numbers. Right now we handle it by having to click on a link go to another page and add the phone numbers. The issue here is your have to have the parent object already saved which means saving a base say employee and then sending to another page etc. etc.

Thanks.

Solution for an "Annotation" Controller?!?

Great tutorial here ...

I work with the petclinc example. And in this example the Controller works with the methods setupForm and processSubmit. No formBackingObject or onSubmit - no Command object ... and so on.

In the JSP the petclinic example works with form:form modelAttribute="..." and form:input path="...".

In the examples with the dynamic list there works with spring:bind path="...".

So ... i am not sure wheter the solutions works also for "my" controllers and jsp's I hope so ... but i am not sure.

Or is it possible to "translate" the solutions into "my way to display a form to the user" ?!? When this is possible it's easier to me when i have an example.

Many Greetings,
Oliver

Trying it out...

Hi Matt and everyone,

Trying the solution above because it looks quite elegant.

I have LazyList similar to your example (Grid):

private List items = ListUtils.lazyList(new ArrayList(),
new Factory(){
public Object create(){
return new Item();
}
});

In my JSP I display one extra item (new item to be created and persisted) when looping through the list. My understanding that LazyList should allow this, right?

But when the JSP is displayed for the first time (not the submission part), I still get IndexOutOfBoundsException exception:


java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
at java.util.ArrayList.RangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at org.hibernate.collection.PersistentList.get(PersistentList.java:271)
at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:556)
at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:441)
at org.springframework.beans.BeanWrapperImpl.getBeanWrapperForPropertyPath(BeanWrapperImpl.java:418)
at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:524)
at org.springframework.validation.AbstractPropertyBindingResult.getActualFieldValue(AbstractPropertyBindingResult.java:77)
at org.springframework.validation.AbstractBindingResult.getFieldValue(AbstractBindingResult.java:337)
at org.springframework.web.servlet.support.BindStatus.(BindStatus.java:117)
at org.springframework.web.servlet.tags.form.AbstractDataBoundFormElementTag.getBindStatus(AbstractDataBoundFormElementTag.java:179)

Am I missing something simple?

Thanks!

Dima

Matt Fleming's picture

Hmm

Yeah. The idea of a lazy list is that you never get a null object or index out of bounds when calling a get kind of function. The only thing I can think of is that by the time you get to the jsp you don't have a LazyList... maybe you called the setItems(List) method on accident. It is really pretty difficult to have any real clue though (for me anyway). I'd debug the request and step through the entire stack just to make sure that everything was happening as I expected.

The other thing I'd do is unit test the List. Write a quick unit test that gets items out of the list to make sure that it is working properly. If you're using the commons-collections list this probably would be overkill, but it is still nice to see things working at a unit level.

-Matt

found it...

Thanks, Matt.

After a bit of debugging, I think I figured it out:

The object that contains the list in question is persisted by Hibernate. Hibernate was replacing the list with PersistentList.

I might have to use some intermediary command object or find other way to get the new item in.

Cheers,

Dima

Matt Fleming's picture

This is why

I only add the object on the front end via javascript and render whatever comes from the db as normal.

-Matt

Binding List

I am new to spring. But I have used Struts Logic Iterator using index to process collections.

Examples are order Items.
A phone contact book.

For eg: we have limit on the number of order items, say 10.
Instead of using commong collections, create 10 empty object and then use the bind tag.. This will work.

cheers

Thanks, that really helped a lot

The LazyList works great, I didn't know about that.
I am using Spring MVC 2.5.1 and the @ModelAttribute annotation on my form Object that holds the list.

Cheers
----
PLATOGO - play together online

Using textarea

Hi, Matt -

Thanks for posting this. It and the additional questions/comments helped tremendously. I have things working for a page in my app just fine. But I would like to use a textarea rather than some flavor of input as I need multiple lines; I have it working using input / text right now. Since textarea does not have a value attribute, no data is displayed. Is there a way to implement this solution for a textarea?

Thanks,

Bill