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:
- 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.
- 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.
- 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.
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.
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
Re: example
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
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
break it down more
You seem to have bitten off more than you can chew..
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:
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
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;
}
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
Re: sequence diagram
That's a pretty good resource. It definitely would have answered my initial question about the sequence which methods were called..
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.
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.
-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):
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:
Am I missing something simple?
Thanks!
Dima
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
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
hvn trouble with spring:bind
hi Matt,
I have a RefreshablePagedListHolder with a list of user details. the list is displayed with paging capabilities in a jsp page. i have included a checkbox to select the necessary users (a checkbox for each user) and bound them as u showed above. my controller is a MultiActionController as i have to handle paging requests as well as the submit request where i have to remove the users selected by the checkboxes.
when using MultiActionController i have to set a url parameter specifying the method to be invoked so i set the window.location at the onclick event of the submit button.
however, the checkbox values are not bound to the bean when the method at the controller is invoked. but the returning bean shows that the previously checked checkboxes as checked. this means that although the spring binding does not occur at the time of the method invocation, the changes made are retained after the processing of the request.
so as i understand, if x is the sequence of method invocation, the changes made at x is actually carried out at the (x + 1)th request.
hope u got my problem clearly. i have been struggling with this problem for a long time and still cudn find a solution. hope u can help me solve this.
thanx in advance
Gayani
did a work-around for the problem
I just did a work-around for the problem. I am checking whether a submission has occured when the page loads and if it is a response to a submission, I am reloading the page. This way I can avoid the problem. However I'm very keen to find what went wrong where in my coding.
not binding properly
Hej
Great article, you helped me make some positive steps in the right direction. I've come across some problems binding my items and getting em back into my class though.
the jsp I'm using is very similar:
and I've got some Javascript that creates/removes rows of (fields, operators and keywords).
I think I've got my collection set up right... The following is part of my SearchFormImpl class which gets set in the onSubmit of my SimpleFormController:
searchFormImpl = (SearchFormImpl)command;But when I submit the form the results don't get bound properly. I added output to my jsp to see whats what and get the following:
Which doesn't make sense because advParams[0].field=content & advParams[1].field=docdatetime should be on different rows (ie different items in the advParams collection). Even worse the advParams is empty onSubmit.
Any thoughts???
Your bind tags look off to me
I think your problem is that you are specifying the wrong name attributes for the actual html elements within the bind tag. It is the name attribute that is used by Spring MVC to do the binding.
E.g
Should be
I'm not sure which version of jstl you are using but you probably don't need the c:out tags either, if you do just add them around the status.xxx calls.
-Matt
still not workin
first, you were right about the c:out tags, I should now go through and check how many other places i've used em unnecessarily
the name values were something that i was playing around with to debug and now that I set em back to ${status.expression} its still not working - I'm going to try removing my code and put your example in verbatim and see if i can't figure out my problem by working in that direction
The way I figure this stuff out is to
Look at the html that is output. You should see something like formBackingObjectName.listName[##].property in the name attribute for the html element. If you don't see that, spring mvc won't bind it. If that doesn't lead me anywhere, the other thing that I do is turn on more verbose logging in the spring mvc components. There is binding debug statements.
I think sometimes we get a little lost in the trees on this kind of stuff and make the data binding process more complicated than it really is, though. You should be able to make your own static html page with the form elements you want to bind and submit it. Spring MVC is really easy to use once the data binding mechanism is truly understood. I'd suggest you creating a simple prototype application where you can experiment with standard data types and then get into your own custom data types to fully understand the process... it sounds like that's what you're going to do...
-Matt
well that was easy
it was in the for each
<c:forEach var="param" items="command.advParams" varStatus="rowItem">I didn't put the ${} around my items
of course now I need a way to show the items once if they come out empty... i suppose i might need some if/else trickery
i guess it wasn't that easy
while that got the elements bound in one direction, I'm still not getting the user input on submit?
I've decided that in referenceData() if there aren't any AdvancedSearchParam bound to my command object then I'll add a new list with a blank AdvancedSearchParam (instead of doing if/else trickery in my jsp). On a fresh page, the jsp file produces html that looks like this:
note the
value="wtf"on advParams[0].keyword, this is because of the new list I'm adding, and the fact that the constructor for AdvancedSearchParam sets the default of keyword to "wtf"my newest thought on where the problem might stem from is in the servlet.xml: