'Spring binding collection to form with hidden field

I'm trying to edit a product. The form backing object is pretty simple:

private Integer           productId;
private String            name;
private Double            price;
private List<Integer>     relatedProductList;  //list of related product ids

...//getters/setter

The part causing the problem is the relatedProductList. I'm trying to put the list on post to display it on a subsequest page. I tried using a hidden field like this in my jsp:

<form:hidden path="relatedProductList"/>

The hidden field appears nicely in the html as you would expect:

<input id="relatedProductList" name="relatedProductList" type="hidden" value="[200408, 200417]"/>

The post data looks good using firebug:

relatedProductList    [200408, 200417]

But in my controller the form backing object has a null product list??

@RequestMapping(method = RequestMethod.POST, value = "/edit.do", params = "editRelatedProducts")
public ModelAndView editRelatedProducts(@Valid @ModelAttribute ProductForm form, BindingResult result) {
    if (result.hasErrors()) {    
        ModelAndView view = new ModelAndView(VIEW_PRODUCT);
        setupCreateReferenceData(view , form);
        return view ;
    }

    ModelAndView editView = new ModelAndView(VIEW_EDIT_RELATED);

    //method to lookup the product ids and place product objects on model
    editView.addObject("relatedProducts",populateProductList(form.getRelatedProductList()));

    return editView ;
}

** But the form.getRelatedProductList is null!

I can get around the problem by using a hidden field and setting the value in the jsp, in the loop that shows the related products:

        <div>
            <table id="relProductTbl" class="tablesorter">
              <thead>
                 ...
              </thead>
              <tbody>
                  <c:forEach var="prod" items="${relatedProducts}" varStatus="row">
                        <tr>
                            <input id="relatedProductList" name="relatedProductList" type="hidden" value="${prod.productId}"/>
                             ...
                        </tr>
                  </c:forEach>
              </tbody>
            </table>
        </div>

This produces the following html:

<input id="relatedProductList" name="relatedProductList" type="hidden" value="200408"/>
...
<input id="relatedProductList" name="relatedProductList" type="hidden" value="200417"/>

Which seems fine and produces the following post:

relatedProductList    200408
relatedProductList    200417

And suddenly the form.getRelatedProductList() is now populated correctly.

Does anyone know why the post data contractList [200408, 200417] doesn't get bound to the form correctly when using springs form:hidden tag? Is this a bug or the expected behaviour. Seems very strange to me just wanted to throw it out there and see if I'm doing it wrong perhaps, or if it can help anyone else.

Thanks.



Solution 1:[1]

I know this is old, but let me briefly answer it:

The relatedProductList [200408, 200417] you observed in the generated HTML <input id="relatedProductList" name="relatedProductList" type="hidden" value="[200408, 200417]"/> is simply the value of relatedProductList.toString(), which in turn uses the default implementation from java.util.AbstractCollection. The format is like this: [..., ..., ...]. Spring MVC uses the toString() method for getting a representation for a form input field, which is quite straight forward: It needs a plain string.

This does not guarantee that it's able to parse that same string later. Obviously, if it can't, it just binds the variable to null.

What you later did (using <input type="hidden"> directly) was actually more a hack, but I'm used to having to do such things sometimes when using Spring MVC since there are some gaps in the functionality.

In general, if there are several form fields with the same name, they all get embedded in the POST data. If you check the POST data string, you'll find something like relatedProductList=200408&relatedProductList=200417 there. Now, when Spring MVC discovers this case while parsing the POST data, it tries to map this to the target variable at hand. If that target variable is a java.util.Collection (as in your case), it uses the values from the POST data as collection elements, which is quite intuitive.

If, just to give another example, the target variable is of type String, it will populate it like this: "200408,200417".

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 TXN