1

我有一个问题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;
    }

}

基础DTO

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和相应的<f:selectItem itemValue="#{null}" itemLabel="--" noSelectionOption="true" />一切正常,但我真的很想把它noSelectionOption放在我的清单上。

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

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

如果我的availableItems变量是 aMap<String, String>而不是 a List

private Map<String, String> 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页面上没有预先选择。

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

在此先感谢您的帮助!

4

2 回答 2

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"因为这确实经常被误解。它可能应该在内部被视为与禁用项目相同的方式。

于 2021-12-16T12:36:50.567 回答
0

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

于 2021-12-11T15:00:43.327 回答