Spring MVC checkbox tag bound to collection expects object but validation expects object.id
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:
- At the time of evaluation of the tag,
<form:checkbox>
needed an expression that evaluated to an actual instance of Class2 - 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.
精彩评论