Spring MVC表单:选项标签没有连接到我的对象Id?

时间:2009-12-18 15:39:03

标签: java spring forms spring-mvc jstl

我正在尝试在列表框中显示我的命令对象集合字段。里面的集合是一个字段,id和名称。我想使用id作为html选项值,使用名称作为选项文本。请参阅以下代码;

<form:select id="customCollection" path="customCollection" size="10">
    <form:options items="${command.customCollection}" itemValue="id" itemLabel="name"/>
</form:select>

名称打印正常,但值保留为空白。这是输出HTML;

<option selected="selected" value="">name-value</option>


我最初的假设是我的数据不正确,但在我的页面中放入以下代码之后;

<c:forEach items="${command.customCollection}" var="c">
    ${c.id} : ${c.name} <br>
</c:forEach>

正确打印出id和名称。所以我的数据正确地传递给我的观点。这让我觉得我要么使用表单:选项不正确,要么在表单中遇到一些错误:选项。

有人可以帮助我吗?

修改
感谢BacMan和delfuego的帮助,我已经能够将这个问题缩小到我的活页夹。

以前我将元素中的值分配给行的名称,这是我的初始绑定器;

binder.registerCustomEditor(Collection.class, "customCollection",
        new CustomCollectionEditor(Collection.class) {

    @Override
    protected Object convertElement(Object element) {
        String name = null;

        if (element instanceof String) {
            name = (String) element;
        }
        return name != null ? dao.findCustomByName(name) : null;
    }
});

当我从initBinder方法中删除此代码时,行值正确地插入到表单中,但我需要一个customEditor来将所述值转换为数据库对象。

所以这是我对活页夹的新尝试;

binder.registerCustomEditor(Collection.class, "customCollection",
        new CustomCollectionEditor(Collection.class) {

    @Override
    protected Object convertElement(Object element) {
        Integer id = null;

        if (element instanceof Integer) {
            id = (Integer) element;
        }
        return id != null ? dao.find(Custom.class, id) : null;
    }
});

但是,这会导致与上一个活页夹相同的行为并使值不显示。关于我在这里做错了什么的想法?

编辑2:
正如我上面提到的,如果我注释掉我的自定义绑定器,那么Custom对象会为表单的视图部分正确加载其id和名称,但是当我尝试保存它时,它永远不会绑定回父对象。所以我真的认为问题出在我的活页夹上。

我在我的convertElement方法中放置了调试语句。一切看起来都应该工作,dao正确地从数据库中提取对象。 唯一令我怀疑的行为是每个自定义项目都会调用convertElement方法两次。

3 个答案:

答案 0 :(得分:4)

这是其中一个问题,一旦我明白出了什么问题,我就不明白它是如何起作用的。

我完全以错误的方式使用CustomCollectionEditor。根据Marten Deinum在this thread,

中的帖子
  

正如我在另一个帖子中所说,CustomCollectionEditor是创建集合(List,Set,?)。因此,它将使用所需类型的元素填充所需的集合。

     

但是,它并不打算将单个元素转换为值。它旨在处理集合,而不是单个Role实例。您希望1个PropertyEditor为您完成2个任务。

所以它为每个元素创建了一个唯一的集合,当它试图生成HTML时,最终在Spring代码中被忽略了。

这就是我最终要做的事情,

binder.registerCustomEditor(Custom.class,
        new PropertyEditorSupport() {

            @Override
            public void setAsText(String text) {
                Custom custom = dao.find(Custom.class,
                        Integer.parseInt(text));
                setValue(Custom);
            }
        });

我不知道为什么我以前的CustomCollectionEditor使用名称作为值。

答案 1 :(得分:2)

我将尝试提供一个工作示例,这可能有助于调试您遇到的问题:

<form:form commandName="client" method="post" action="${edit_client}">
    <form:select path="country">
        <form:options items="${requestScope.countries}" itemValue="id" itemLabel="name"/>
    </form:select>
</form:form>

这是我的Country类,它是我的命令对象中的成员变量。

public class Country {

    private Integer id;
    private String name;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(final Object anObject) {
        if (anObject == null) {
            return false;
        } else if (this == anObject) {
            return true;
        } else if (anObject instanceof Country) {
            final Country aCountry = (Country) anObject;
            Integer aCountryId = aCountry.getId();
            if (aCountryId != null) {
                return aCountry.getId().equals(id);
            }
        }
        return false;
    }

    @Override
    public int hashCode() {
        return id;
    }
}

我在Controller的initBinder方法中使用自定义属性编辑器。我将省略实现,因为它使用了通用实现。

binder.registerCustomEditor(Country.class, "country", editorServiceFactory.getPropertyEditor(Country.class, CustomPropertyEditor.class));

这是参考数据(此方法是从Controller的referenceData方法调用的):

public Map<String, List<?>> getDemographicReferenceData() {
    Map<String, List<?>> referenceData = new HashMap<String, List<?>>();
    referenceData.put("countries", countryDAO.findAll());
    return referenceData;
}

我正在使用Spring 2.5

答案 2 :(得分:1)

对我来说似乎不对的一件事是command.customCollection用于填充表单选择输入的可能值并绑定到用户选择的最终值。选择输入。这没有任何意义,至少对我而言......例如,如果我有一个表单选择选择美国地址状态,我会用一组有效状态填充该选择,但我会绑定select到 one 状态的值,最终由用户选择。

尝试此操作:在customCollection对象的上下文之外创建command对象。换句话说,现在您的customCollectioncommand对象的属性;而不是这个,将该对象从command对象中拉出并使其成为自己的页面属性。在Spring MVC模型中,类似于将用作下拉数据源的东西通常被称为参考数据;在SimpleFormController中,此数据将以适当命名的SipleFormController#referenceData方法填充。这将两个不同的概念分开 - 参考数据中的select live的可能值,以及用户在绑定到表单和/或选择输入的命令对象中选择的最终值。

假设这是SimpleFormController,请尝试添加(或适当修改)referenceData

@Override
protected Map<?, ?> referenceData(HttpServletRequest request, Object command, Errors errors) throws Exception {
  CustomCollection customCollection = new CustomCollection();
  // ...populate your custom collection
  request.setAttribute("customCollection", customCollection);
  return super.referenceData(request, command, errors);
}

然后将您更改为:

<form:select id="customCollection" path="command" size="10">
  <form:options items="${customCollection}" itemValue="id" itemLabel="name"/>
</form:select>

这有意义吗?