【问题标题】:JSF SelectManyListbox with noSelectionOption="true" - Validation Error: Value is not valid带有 noSelectionOption="true" 的 JSF SelectManyListbox - 验证错误:值无效
【发布时间】:2021-11-22 10:24:14
【问题描述】:

h:selectManyListbox 有问题,当项目填充 POJO 并且 noSelectionOption 为真时(对于 h:selectManyListbox 以枚举作为项目,它按我预期的那样工作)。

@Named
@ViewScoped
public class MyBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private List<BaseDTO> availableItems = null;

    private String[] selectedItems = null;

    @PostConstruct
    private void initialize() {
        loadAvailableItems();
    }

    private void loadAvailableItems() {
        availableItems = Arrays.asList(new BaseDTO("entityId", "entityDescription"), new BaseDTO(...), ...);
    }

    public List<BaseDTO> getAvailableItems() {
        return availableItems;
    }
    
    public String[] getSelectedItems() {
        return selectedItems;
    }

    public void setSelectedItems(String[] selectedItems) {
        this.selectedItems = selectedItems;
    }

}

BaseDTO

public class BaseDTO {

    private String id;

    private String description;

    public BaseDTO(String id, String description) {
        this.id = id;
        this.description = description;
    }

    public String getId() {
        return id;
    }

    public String getDescription() {
        return description;
    }

    @Override
    public String toString() {
        return id;
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id.hashCode();
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        BaseDTO other = (BaseDTO) obj;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        return true;
    }

}

XHTML

<h:selectManyListbox value="#{myBean.selectedItems}" hideNoSelectionOption="false" size="4">
    <f:selectItem itemValue="#{null}" itemLabel="--" noSelectionOption="true" />
    <f:selectItems value="#{myBean.availableItems}" var="entry" itemValue="#{entry.id}" itemLabel="#{entry.description}" />
</h:selectManyListbox>

当我尝试提交页面时,我总是得到Validation Error: Value is not valid。 如果我删除 hideNoSelectionOption 和相应的 &lt;f:selectItem itemValue="#{null}" itemLabel="--" noSelectionOption="true" /&gt; 一切正常,但我真的很想在我的列表中有这个 noSelectionOption

我尝试使用 OmniFaces SelectItemsConverter 甚至创建自己的自定义转换器,但没有成功。无论我尝试什么,我都无法克服这个验证错误。

同时我发现了一个不太好的解决方法:

如果我的availableItems 变量是Map&lt;String, String&gt; 而不是List

private Map&lt;String, String&gt; availableItems = null;

如果我在地图中添加一个空条目:

    private void loadAvailableItems() {
        List<BaseDTO> dtoList = Arrays.asList(new BaseDTO("entityId", "entityDescription"));
        availableItems = dtoList.stream().collect(Collectors.toMap(BaseDTO::getId, BaseDTO::getDescription));
        availableItems.put(null, "--");
    }

然后,一切都按预期进行,除了页面上没有预先选择 noSelectionOption

这是预期的组件行为,还是我遗漏了什么?

提前感谢您的帮助!

【问题讨论】:

  • 我建议使用实体类,如“Item”、“Entry”或其他实体类(不一定是 JPA 实体类)和存储库(有时也称为服务),而不是 BaseDTO您可以从中访问实体的 ItemRepo 或 ItemService。然后,您可以将 repo/service 注入您的支持 bean MyBean 并在 @PostConstruct 方法中填充实体列表。如果您发布自定义转换器的代码也会很有帮助。
  • 我的 BaseDTO 类是一个 POJO(不是 JPA 实体类),它将数据从持久层传送到视图。 MyBean 上的 List 变量是使用服务填充的,就像您建议的那样 - 为了清楚起见,我的示例进行了简化。请注意,如果我删除 noSelectionOption,一切正常。

标签: validation jsf converters mojarra selectmanylistbox


【解决方案1】:

首先,noSelectionOption/hideNoSelectionOption 属性对在这里被误解了。请删除它们。它在您的上下文中完全没有用。为了更好的理解他们的初衷,前往Best way to add a "nothing selected" option to a selectOneMenu in JSF的回答,总结如下:

此属性对的主要目的是防止网站用户在组件已经选择了非空值时能够重新选择“无选择选项”。

在您的特定情况下,您有一个多选列表框。首先,在这样的用户界面元素中设置“未选择”选项是没有意义的。您只需取消选择所有内容即可获得“未选择”状态。这在例如单选下拉列表中是不可能的,因为您不能首先取消选择选定的选项。因此,此类用户界面元素需要“未选择”选项。但是,再一次,多选列表框不需要这样做。但是,我确实知道拥有一个可操作的元素会自动取消选择列表框中的所有内容是很有用的。这可以通过列表框附近某处的链接或按钮来完成。

无论如何,我已经能够在 Mojarra 2.3.17 中重现所描述的问题。根本问题是“空字符串提交的值”不再由空字符串数组表示,而是由具有单个项目的字符串数组表示,一个空字符串。因此,所有与“提交的空字符串值”相关的检查都失败了。我不认为这是 JSF 本身的错误,而只是意外使用了多选组件。

您可以通过在渲染响应阶段(第 6 阶段)以外的所有阶段显式禁用该项目来解决这一切。它将是可选的,但会自动从所选项目中删除,作为针对篡改请求的内置措施。这样“空字符串提交的值”将是一个空数组,正如预期的那样。

<h:selectManyListbox value="#{myBean.selectedItems}" size="4">
    <f:selectItem itemValue="#{null}" itemLabel="--" itemDisabled="#{facesContext.currentPhaseId.ordinal ne 6}" />
    <f:selectItems value="#{myBean.availableItems}" var="entry" itemValue="#{entry.id}" itemLabel="#{entry.description}" />
</h:selectManyListbox>

需要注意的是,这不能通过自定义转换器或验证器来解决。 JSF 不允许自定义转换器返回 null,并且这个特定的“值无效”验证由内置验证器针对无法替换/禁用的篡改请求完成。我们最好的选择可能是更改/重新指定noSelectionOption="true" 的行为,因为这确实经常被误解。它可能应该在内部被视为与禁用项目相同的方式。

【讨论】:

  • 你是绝对正确的——首先在这样的用户界面元素中设置“未选择”选项是没有意义的。问题的产生是因为我无法理解如何取消选择单个选定的选项。显然,它就像选择(使用 Ctrl)一样简单。再次感谢您!
【解决方案2】:

看起来它正在尝试验证该值,但它不能。您是否尝试过使用自定义验证器?

【讨论】:

  • 这是问题问题的答案吗?如果您的意图是发布答案,请更加肯定,并且不要在答案中留下问号,否则您的答案将被机器人标记为删除。
  • 是的,正如我所写,我尝试使用 OmniFaces SelectItemsConverter 甚至创建自己的自定义转换器,但没有运气。
  • 您说的是 SelectItemsConverter,但我想问您是否尝试过使用自定义验证器。请注意,Faces 进行转换(使用 Converter API),但也进行验证(使用 Validator API)。并且错误消息似乎表明验证失败。
猜你喜欢
  • 2011-02-02
  • 2017-11-10
  • 2011-09-13
  • 2013-05-20
  • 1970-01-01
  • 2011-06-01
  • 1970-01-01
  • 2017-10-31
相关资源
最近更新 更多