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.

problem with binding

Hi Matt,
I have a jsp page which has a button for invoice entry, once after clicking the button I want to post a new row which has some cells in it, should be binded with the spring. My doubt is, how to bind row which is created by java script function?
Can you please help me out with suitable code.

thanking you,
Manojkumar

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

mudzakkir's picture

The link is broken,

The link is broken, Matt..
http://blog.springframework.com/rob/wp-content/advancedspringmvc.pdf
Can i download the netbeans project for your post please? Or can you post the article more detail?
Kejujuran adl perhiasan yg plg indah.Saya yakin Anda adl salah satu dari org-org yg menyukai kejujuran.Dan Anda-lah salah satu dr org-org yg jujur.

Matt Fleming's picture

Just google it

I don't think I have a copy anywhere, but searching for advanced spring mvc found a bunch of them.

-Matt

Bind to Javascript dynamic Rows

Hi,

I am having similar issue. I have to generate Table Rows dynamically through Javascript.

How can I bind these rows to spring model.

Please help me. I am stuck with this issue for 2 days. Thanks.

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.

Hi, Can you please let me

Hi,
Can you please let me know how did you resolve your problem.
I seem to have the same problem. When my form is validated and contains some errors, all my children rows are lost, however I have all my parent fields preserved when form error is shown. Only the child fields are missing.

One more strange problem is that when i add a new child via javascript, the controller sees it fine, but when I add same child from AJAX, it adds one more child with all its fields set to null. So in effect I have two children one of which is valid child and another one is mysteriously generated from nowhere.

Can you please help me out? I am using spring framework 3.0.2 release with annotated controllers

Thanks
Ahsan

Hi, Can you please let me

ahsan_cse2004 wrote:
One more strange problem is that when i add a new child via javascript, the controller sees it fine, but when I add same child from AJAX, it adds one more child with all its fields set to null. So in effect I have two children one of which is valid child and another one is mysteriously generated from nowhere.

I was doing it wrong. In my AJAX call I was generating loopIndex starting from 1 not from 0, hence spring was trying to bind elements from 0 to 1.

Thanks
Ahsan

Hi, I have the same problem.

Hi,
I have the same problem. I am unable to delete the children rows because they are already present in my collection. I am trying to delete few of the children using javascript.

Can you please help me out how did you manage to resolve this?

Also, I am using spring 3.0.2 with annotation feature so don't know how to override onBind etc.

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

You mean that if form does not contain any error, you are able to get only non deleted children in your controller?

Thanks
Ahsan

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.<init>(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

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:

<c:forEach var="param" items="command.advParams" varStatus="rowItem">
<span id="multi_search_fields">
	<c:set var="actual" value="${rowItem.index}"></c:set>
	<!-- Field types -->
	<spring:bind path="command.advParams[${rowItem.index}].field">
  		<select name="advParams[${rowItem.index}].field" id="additional_multi_search_field_${rowItem.index}"  onChange="updateOperators(this)">
			<c:forEach var="searchField" items="${command.searchFieldList}">	
				<option <c:if test="${searchField.name == status.value}">selected</c:if> value="${searchField.name}">
					<c:out value="${searchField.display}" />
				</option>
			</c:forEach>		
		</select>
	</spring:bind>
 
	<!-- Operators -->
  		<select name="advParams[${rowItem.index}].operator" id="additional_search_operator_${rowItem.index}" >
		</select>
 
	<!-- Keywords -->
	<spring:bind path="command.advParams[${rowItem.index}].keyword">
  		<input type="text" name="advParams[${rowItem.index}].keyword" value="<c:out value='${status.value}'/>"
			id="additional_multi_search_value_${rowItem.index}"  onkeydown="doOnEnter(event,submitSearch);"/>
  	</spring:bind>
</span>
</c:forEach>

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;

	private Collection<AdvancedSearchParam> advParams;
 
	public void setAdvParams(Collection<AdvancedSearchParam> advParams) {
		this.advParams = advParams;
	}
	public Collection<AdvancedSearchParam> getAdvParams() {
		return advParams;
	}

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:
<c:forEach var="param" items="command.advParams" varStatus="rowItem">
<span id="multi_search_fields">
	<c:set var="actual" value="${rowItem.index}"></c:set>
 
row: <c:out value="${rowItem.index}" /> 
field: <c:out value="${param}" /> <br/>
 
	<!-- Field types -->
	<spring:bind path="command.advParams[${rowItem.index}].field">
.......

row: 0 field: {advParams[0].field=content, advParams[0].keyword=test, advParams[1].field=docdatetime, advParams[1].keyword=wef, advParams[1].operator==, advParams[0].operator==} 

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???

Matt Fleming's picture

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

<spring:bind path="command.advParams[${rowItem.index}].keyword">
  	<input type="text" name="advParams[${rowItem.index}].keyword" value="<c:out value='${status.value}'/>"
		id="additional_multi_search_value_${rowItem.index}"  onkeydown="doOnEnter(event,submitSearch);"/>
</spring:bind>

Should be

<spring:bind path="command.advParams[${rowItem.index}].keyword">
  	<input type="text" name="${status.expression}" value="${status.value}"
		id="additional_multi_search_value_${rowItem.index}"  onkeydown="doOnEnter(event,submitSearch);"/>
</spring:bind>

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

Matt Fleming's picture

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:

<div style="float: left;">
<span id="multi_search_fields">
	<select id="additional_multi_search_field_0" onchange="updateOperators(this)" name="advParams[0].field">
		<option value="blank"> </option>
		.....
	</select>
	<input id="additional_search_operator_0" type="text" value="" name="advParams[0].operator"/>
	<input id="additional_multi_search_value_0" type="text" onkeydown="doOnEnter(event,submitSearch);" value="wtf" name="advParams[0].keyword"/>
</span>
</div>

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"

protected Map<String, Object> referenceData(HttpServletRequest request, Object command, Errors errors) throws Exception {
	log.info("SearchFormController executes referenceData() method");
	searchFormImpl = (SearchFormImpl)command;
	if(searchFormImpl.getAdvParams().size() < 1){
		List<AdvancedSearchParam> ls = ListUtils.lazyList(
			new ArrayList<AdvancedSearchParam>(), new Factory() {
					public Object create(){return new AdvancedSearchParam();
				}});
		searchFormImpl.setAdvParams(ls);
	}
	......

my newest thought on where the problem might stem from is in the servlet.xml:

<bean id="searchFormController" class="sfe.ecm.form.SearchFormController">
	<property name="commandClass" value="sfe.ecm.form.SearchFormImpl"/>
	<property name="formView" value="sfe.simple"/>
	<property name="successView" value="sfe.results"/>
</bean>
but since I've followed your suggestion on turning the logging on, I'll show you what I got, because the mvc debugs don't make much sense to me:
 /** THESE ARE ON GET **/
DEBUG [http-8080-2] (SearchFormController.java:44) - formBackingObject(): SearchFormController creates new SearchFormImpl in formBackingObject() method
DEBUG [http-8080-2] (PropertiesSupport.java:93) - Loaded configuration properties: {sort_date=sort_date, sort_descending=descending, search_type_phrase=phrase, search_type_any=simpleany, profiles=globalsppublished,websppublished,ecmsppublished, sortBy=relevancy,docdatetime, hits_per_page=5,10,20,25,30, search_type_all=simpleall, sort_ascending=ascending}
 INFO [http-8080-2] (SearchFormController.java:90) - SearchFormController executes referenceData() method
 
 /** THESE ARE ON POST **/
DEBUG [http-8080-4] (DispatcherServlet.java:783) - DispatcherServlet with name 'ecm_sfe' received request for [/ecm_sfe/search.html]
DEBUG [http-8080-4] (DispatcherServlet.java:845) - Bound request context to thread: org.apache.catalina.connector.RequestFacade@158015a
DEBUG [http-8080-4] (DispatcherServlet.java:1045) - Testing handler map [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@1e72cae] in DispatcherServlet with name 'ecm_sfe'
DEBUG [http-8080-4] (AbstractUrlHandlerMapping.java:161) - Looking up handler for [/search.html]
DEBUG [http-8080-4] (DispatcherServlet.java:1085) - Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@200309]
DEBUG [http-8080-4] (DispatcherServlet.java:1085) - Testing handler adapter [org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter@36eb76]
DEBUG [http-8080-4] (SearchFormController.java:44) - formBackingObject(): SearchFormController creates new SearchFormImpl in formBackingObject() method
DEBUG [http-8080-4] (BeanWrapperImpl.java:456) - Creating new nested BeanWrapper for property 'advParams[0]'
DEBUG [http-8080-4] (BeanWrapperImpl.java:466) - Using cached nested BeanWrapper for property 'advParams[0]'
DEBUG [http-8080-4] (BeanWrapperImpl.java:466) - Using cached nested BeanWrapper for property 'advParams[0]'
DEBUG [http-8080-4] (TypeConverterDelegate.java:339) - Converting String to [int] using property editor [org.springframework.beans.propertyeditors.CustomNumberEditor@21c3b0]
DEBUG [http-8080-4] (TypeConverterDelegate.java:339) - Converting String to [int] using property editor [org.springframework.beans.propertyeditors.CustomNumberEditor@21c3b0]
DEBUG [http-8080-4] (TypeConverterDelegate.java:339) - Converting String to [int] using property editor [org.springframework.beans.propertyeditors.CustomNumberEditor@21c3b0]
DEBUG [http-8080-4] (SimpleFormController.java:266) - No errors -> processing submit
 INFO [http-8080-4] (SearchFormController.java:50) - onSubmit(): SearchFormController executes onSubmit() method 

for those following along

the resolution to my issue came from setting the id of my elements to ${status.expression}

<spring:bind path="command.advParams[${rowItem.index}].keyword">
  	<input type="text" name="${status.expression}" value="${status.value}"
		id="${status.expression}"  onkeydown="doOnEnter(event,submitSearch);"/>
</spring:bind>

this was a bit suprising because elements with different ids bind fine everywhere else in the application

Matt Fleming's picture

I always forget when

to use name or id in the elements. I think I mostly set both fields to the same thing so I don't have to remember. Glad you figured it out.

-Matt

Bound value not present

I can get rid of the ArrayOutOfBoundsException by using a LazyList, but instead of getting the newly added value of NameType from the form I am submitting, I get a new blank NameType object on the final iteration of the jstl for loop. I am using my own custom propery editor (BigDecimal -> some meaningful String) and so although the value is there if I do:

<c:for var="iter" items="${a.b}">
  ${iter.c.d}
</c:for>

(I get say John Smith on the last iteration)

if I do

<c:for items="${a.b}" varStatus="vs">
  <spring:path bind="a.b[${vs.index}].c.d">
   ${status.value}
  </spring>
</c:for>

the value is simply not there, it instead creates a new blank object.

Memory Usage

Doesn't this method make it trivial to bring down your web server? You end up with form elements with names like grid.blocks[0].id - just changing this to grid.blocks[99999999999].id (in firebug or whatever) before posting the form will allocate a ton of memory as the lazy list attempts to grow...

Matt Fleming's picture

Sure that's possible

Sure that's certainly possible. All you'd need to do is protect the back end by making a validator which catches the condition though. In general you should be protecting the back end from any untrusted client (like a web page). For instance, people who use hibernate as the persistence mechanism also should protect against reachability issues, by making sure that cascading is set up appropriately. This way someone can't modify an object by reachability via a post.. e.g. grid.user.id = 1 and grid.user.password = xxxx. I guess what I'm saying is that you should always protect and secure the back end, no matter how the front end is rendered.

-Matt

incorporating javascript with lazylist

Hello Matt, is there anyway we can see an example of how the javascript binds into the list? Like an addRow function? I would like to see how if say you add 5 rows to a form and then hit save on the form which is sent to a multiactioncontroller method, how those javascript rows actually add on to the lazylist. Thank you!