我有一个域模型,其中包含许多“元素”,可以呈现的文本片段以显示丰富的内容。有HTML文本,纺织文本,Flash对象等。这些元素的基本功能封装在AbstractElement
中,其中包含实现HTMLElement
,FlashElement
等等。因此,模型有一个List<AbstractElement>
来包含它可以拥有的所有元素。
编辑模型时,我希望用户能够动态添加元素,并在用户提交表单时保存这些元素。所以我所拥有的是一个可以通过一些JavaScript动态扩展的表单,其形式如下:
<form action=...>
<!-- Other attributes -->
<textarea name="object.elements[0].content"/>
<textarea name="object.elements[1].content"/>
<!-- Some elements are based on text, others on files -->
<input type="hidden" name="object.elements[2].file" value="somevalue"/>
<textarea name="object.elements[3].content"/>
<!-- Submit button -->
</form>
提交表单时出错。很明显为什么 - 提交时,Spring会尝试实例化列表中的必需元素。由于元素列表包含抽象类型为AbstractElement
的对象,因此Spring无法实例化新元素。
我如何让Spring实例化适当类型的元素?可以在表单中添加类型信息,并有一些ModelAttribute吗?那会怎么样?我可以在模型中做些什么来自动执行此操作吗?
答案 0 :(得分:1)
听起来您需要创建一个(或多个)客户属性编辑器,这些编辑器可以获取请求参数并将它们转换为正确的类实例以添加到集合中。
Spring使用向数据绑定器注册的PropertyEditor实例来绑定请求参数。如果默认的PropertyEditors集合无法确定正确的类型,您可以注册自己的编辑器来处理逻辑。
此链接中的Spring文档中描述了该过程:
http://static.springsource.org/spring/docs/current/spring-framework-reference/html/validation.html
具体来说,请参阅5.4.2.1节,了解如何注册客户资产编辑。
您可以使用控制器的initBinder()方法中的WebDataBinder类的registerCustomEditor()方法注册属性编辑器(使用@InitBinder注释标识)。
答案 1 :(得分:1)
我最终通过实现一个ModelAttribute来解决这个问题,该ModelAttribute通过解析原始表单数据来构造元素列表。解析原始表单数据可以通过使用HttpServletRequest
对象,循环遍历参数映射并仅手动创建所需对象来使用。如果你把它放在一个辅助函数中它是可以重用的,尽管我在每个使用它的控制器中都需要一个ModelAttribute。
这不是理想的解决方案,但它确实有效。
答案 2 :(得分:0)
您可以尝试通过创建一个返回具体对象的ModelAttribute方法来自己实例化formObject。 e.g。
@ModelAttribute("bindingObj")
public AbstractObject initElementList() {
AbstractObject obj = new ConcreteObject();
obj.setElements(new ArrayList<ConcreteElement>);
return obj;
}
@RequestMapping(...)
public ModelAndView requestHandler(@ModelAttribute("bindingObj") AbstractObject) {
....
}
Spring首先调用initElementList并将结果添加到内部modelMap。然后使用此值调用requestHandler。
这种方法的缺点是你最终会得到对具体类的引用。但是如果你有从你的基本控制器派生的子类,那么它可能会有帮助。