开发者

Spring MVC checkbox tag bound to collection expects object but validation expects object.id

开发者 https://www.devze.com 2023-01-25 22:10 出处:网络
In my Spring MVC project I have an update page for Class1 that must display a list of form:checkbox tags that is bound to a collection of entities on Class1.

In my Spring MVC project I have an update page for Class1 that must display a list of form:checkbox tags that is bound to a collection of entities on Class1.

Class1.java:

class Class1 {
  private Set<Class2> set;
  //... other fields 
}

In updateclass1.jspx:

<c:forEach items="${allClass2Instances}" var="class2">
   <form:checkbox label="${class2.name}" path="set" value="${class2}"/><br/>
</c:forEach>

With the checkbox tag as above, when I display the page, the checkbox is ticked if the Class2 instance is part of the Set on class1, and unticked if it isn't. But when I hit submit, I get the following error:

Failed to convert property value of type 'java.lang.String[]' to required type 'java.util.Set' for property 'set'; nested exception is org.springframework.core.convert.ConversionFailedException: Unable to convert value "Name 1" from type 'java.lang.String' to type 'java.lang.Long'; nested exception is java.lang.NumberFormatException: For input string: "Name1"

As far as I can tell when the page is populated, the form:checkbox tag needs an instance to set the correct checked/unchecked value, but on submit the JSP is sending an array of class2.toString() values to a converter that expects the IDs. Conversely when I change the tag to the following:

<form:checkbox label="${class2.name}" path="set" value="${class2.id}"/><br/>

The binding works fine, but when I view the update page the checkboxes are not ticked / unticked correctly because the tag does not know that value being passed in is the object id.

How do I make the binding after submit consistent with what the checkbox tag expects?

In case it ma开发者_JAVA技巧tters - this is all inside a scaffolding page generated by Roo.


Figured out how to make this work for now. If anyone comes up with a neater solution please add it and I'll mark it correct instead.

The problem above was being caused by needing ${InstanceOfClass2} to evaluate to different things in different places:

  1. At the time of evaluation of the tag, <form:checkbox> needed an expression that evaluated to an actual instance of Class2
  2. After the tag completed, the value attribute of the generated <input type="checkbox"> tag needs to be equal to a numerical ID field of an instance of Class2

The solution was to add a converter to my Class1Controller, eg:

Converter<Class2, String> getClass2Converter() {
    return new Converter<Class2, String>() {
        public String convert(Class2 instance) {
            return "" + instance.getId();
        }
    };
}

Thus the expression ${InstanceOfClass2} evaluates to a Class2 instance for the checkbox tag, but when it comes to writing the actual HTML is converted to a numerical ID.

This approach is very messy when working with Roo. All of the other scaffolding relating to Class1 then wants to use this same Converter, so I started seeing a whole bunch of IDs everywhere that you would want to see Class2.name or other such fields. I solved this by modifying the Spring Roo <field:display> custom tag - added an attribute fmtCollectionToString that if present forces the tag to evaluate collections by iterating them and calling toString on each element, instead of calling spring:eval on the whole collection, which also seems to end up with the Converter being invoked.

Like I say, neater solutions greatly appreciated! If there's a way of making converters behave differently in different circumstances, for instance - still want to hear it.

0

精彩评论

暂无评论...
验证码 换一张
取 消