使用 Boolean 类型的 Converter 时,Spring 复选框 JSP 标签被破坏

发布于 2024-11-18 13:42:31 字数 1460 浏览 6 评论 0原文

我已经使用 Spring Roo 和 Spring MVC 设置了一个 CRUD Web 应用程序。我的问题是:由于我使用转换器来本地化布尔值的显示,因此 Spring JSP 标签 checkbox 被破坏,这意味着复选框不会从支持 bean 获取实际值。它们总是虚假的且未经检查的。

我做了一些研究,可能在 org.springframework.web.servlet.tags.form.CheckboxTagwriteTagDetails 方法中发现了错误。这是此方法有趣的部分:

// the concrete type may not be a Boolean - can be String
if (boundValue instanceof String) {
    boundValue = Boolean.valueOf((String) boundValue);
}
Boolean booleanValue = (boundValue != null ? (Boolean) boundValue : Boolean.FALSE);
renderFromBoolean(booleanValue, tagWriter);

因为我使用转换器来显示是/否而不是 true/false,所以 boundValue 是一个字符串,并且调用 Boolean.valueOf 总是导致 false,因为 valueOf 方法不知道所使用的 Spring Converter 并将 yes/no 解释为 false。

我该如何用 Spring 解决这个问题?有人有线索吗?我的大脑已经走进了死胡同。

只是为了完整性:布尔类型的转换器按预期工作(代码见下文)。

public class BooleanConverter implements Converter<Boolean,String>, Formatter<Boolean> {

@Autowired
private MessageSource messageSource;

@Override
public String print(Boolean object, Locale locale) {
    return (object)
            ? messageSource.getMessage("label_true", null, LocaleContextHolder.getLocale())
            : messageSource.getMessage("label_false", null, LocaleContextHolder.getLocale());
}

@Override
public String convert(Boolean source) {
    return this.print(source, null);
}
}

I've set up a CRUD web application with Spring Roo and Spring MVC. My problem is: since I'm using a converter for localizing the displaying of boolean values the Spring JSP Tag checkbox is broken which means the checkboxes don't take the real value from the backing beans. They are always false and unchecked.

I've done some research and probably found the error in the writeTagDetails method of org.springframework.web.servlet.tags.form.CheckboxTag. Here is the interesting part of this method:

// the concrete type may not be a Boolean - can be String
if (boundValue instanceof String) {
    boundValue = Boolean.valueOf((String) boundValue);
}
Boolean booleanValue = (boundValue != null ? (Boolean) boundValue : Boolean.FALSE);
renderFromBoolean(booleanValue, tagWriter);

Because I'm using a converter to display yes/no instead of true/false the boundValue is a String and the call of Boolean.valueOf always results in false because the valueOf method isn't aware of the used Spring Converter and interprets yes/no as false.

How can I solve this issue with Spring? Has anybody a clue? My brain has reached a blind alley.

Just for completeness: The converter for the Boolean type is working as expected (code see below).

public class BooleanConverter implements Converter<Boolean,String>, Formatter<Boolean> {

@Autowired
private MessageSource messageSource;

@Override
public String print(Boolean object, Locale locale) {
    return (object)
            ? messageSource.getMessage("label_true", null, LocaleContextHolder.getLocale())
            : messageSource.getMessage("label_false", null, LocaleContextHolder.getLocale());
}

@Override
public String convert(Boolean source) {
    return this.print(source, null);
}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

浮生面具三千个 2024-11-25 13:42:31

这似乎是可以克服的。也就是说,您希望人类可读的格式化程序向用户显示模型中布尔值的是/否。但是您仍然希望复选框 HTML 元素正常工作,并且似乎这些 HTML 复选框元素/小部件/JSP 标记期望使用 true/false 字符串(或布尔 Java 类型),它似乎没有使用转换器来获取任意值yes/no 字符串返回布尔类型。

对我来说,这个问题表现为当模型设置了 Boolean.TRUE 值时,复选框的初始状态永远不会被选中。这意味着记录的任何读取-修改-更新(如果不编辑该字段,当用户未更改该字段时,最终会从“true”转换为“false”)。这是因为即使模型处于真实状态,UI 中的初始状态也与模型不一致(它始终显示未选中的状态,即错误状态)。即使模型的该值具有 Boolean.TRUE,显示的值也是 HTML 编辑记录屏幕中未选中的复选框。这是因为 HTML 复选框元素不会将“yes”解释为“true”,并且它默认为 false(因为这是默认的布尔值)。

因此,定义您的格式化程序/转换器(就像您已经在做的那样)。但是在您的@Controller中添加:

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Boolean.class, "friesWithThat", new CustomBooleanEditor(false));
}

这似乎使字符串显示值继续为是/否,但使用并传递给复选框HTML元素的值仍然为真/假。

现在,当编辑/更新记录(在 CRUD 中)时,复选框的初始状态与模型一致,并且保存数据(不编辑任何字段)不会转换复选框状态(这是我对您遇到的问题的理解)。

因此,从这里我认为我们可以理解转换器/格式化程序用于数据的一般显示,而属性编辑器用于映射模型数据,以便 UI 小部件所需的数据。

This seems possible to overcome. That is you want a human readable Formatter to display yes/no to the user for boolean values in the model. But you still want the checkbox HTML elements to work and it appears those HTML checkbox elements/widgets/JSP tags expects true/false string to be used (or a boolean Java type) it doesn't appear to use the converter to get the arbitrary yes/no string back to a boolean type.

This problem for me manifests itself as the initial state of the checkbox is never ticked when the model has a Boolean.TRUE value set. This means any read-modify-update of the record (without editing that field ends up transitioning from 'true' to 'false' when it was not changed by the user). This is due to the initial state in the UI being inconsistent with the model (it shows always unchecked i.e. false state) even when model is true state. The displayed value is of an unchecked checkbox in the HTML edit record screen, even when the model has Boolean.TRUE for that value. This is because "yes" does not get interpreted as "true" by the HTML checkbox elements and it defaults to false (as that is the default boolean value).

So define your Formatter/Converter (as you are already doing). But in your @Controller add:

@InitBinder
public void initBinder(WebDataBinder binder) {
    binder.registerCustomEditor(Boolean.class, "friesWithThat", new CustomBooleanEditor(false));
}

This appears to make the string display value continue to be yes/no but the value used and passed to the checkbox HTML elements continues to be true/false.

Now when editing/updating the record (in CRUD) the initial state of the checkbox is consistent with the model and saving the data (without editing any fields) does not transition the checkbox state (which is my understanding of the problem you have).

So from this I think we can understand that Converters/Formatters are for general display of data and that PropertyEditors are for mapping model data so the data needed by the UI widget.

舂唻埖巳落 2024-11-25 13:42:31

也许您应该编写自己的类型,名为 Choice ,它具有 Choice.YESChoice.NO 作为序数枚举,对应于基于 1 或 0 的存储在数据库中的值。

然后,您可以在应用程序中为此类型定义自己的显示标签和输入标签,以解决此问题。

Probably you should write your own type called Choice which has Choice.YES and Choice.NO as ordinal enumerations which correspond to 1 or 0 based on the values stored in the database.

Then you can have your own display tags and input tags defined within the application for this type which would address this issue.

千秋岁 2024-11-25 13:42:31

添加到前面的答案,从 spring 3.2 开始,您可以为所有控制器和所有布尔字段注册属性编辑器,如下所示:

package your.package.path;

import org.springframework.beans.propertyeditors.CustomBooleanEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class GlobalBindingInitializer {

    @InitBinder
    public void registerCustomEditors(WebDataBinder binder, WebRequest request) {
        binder.registerCustomEditor(Boolean.class, new CustomBooleanEditor(false));
    }
}

如果您来自 Spring Roo 基本配置,还记得在 webmvc-config.xml 中添加此行

    <context:include-filter expression="org.springframework.web.bind.annotation.ControllerAdvice" type="annotation"/>

,如下所示:

<context:component-scan base-package="your.package.path" use-default-filters="false">
    <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    <context:include-filter expression="org.springframework.web.bind.annotation.ControllerAdvice" type="annotation"/>
</context:component-scan>

Adding to the previous answer, from spring 3.2 you can register the property editor for all controllers and all Boolean fields like this:

package your.package.path;

import org.springframework.beans.propertyeditors.CustomBooleanEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.context.request.WebRequest;

@ControllerAdvice
public class GlobalBindingInitializer {

    @InitBinder
    public void registerCustomEditors(WebDataBinder binder, WebRequest request) {
        binder.registerCustomEditor(Boolean.class, new CustomBooleanEditor(false));
    }
}

if you are coming from Spring Roo basic configuration remember also to add this line in your webmvc-config.xml

    <context:include-filter expression="org.springframework.web.bind.annotation.ControllerAdvice" type="annotation"/>

like this:

<context:component-scan base-package="your.package.path" use-default-filters="false">
    <context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
    <context:include-filter expression="org.springframework.web.bind.annotation.ControllerAdvice" type="annotation"/>
</context:component-scan>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文