JSF SelectManyListbox с noSelectionOption = "true" - ошибка проверки: недопустимое значение.

У меня проблема, когда элементы заполнены POJO и истинно (для 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 и соответствующие <f:selectItem itemValue="#{null}" itemLabel="--" noSelectionOption="true" /> все работает нормально, но мне бы очень хотелось, чтобы это было в моем списке.

Я попытался использовать OmniFaces SelectItemsConverter и даже создать свой собственный конвертер, но безуспешно. Что бы я ни пытался, я не могу преодолеть эту ошибку проверки.

Тем временем я нашел не очень приятный обходной путь:

Если мой availableItems переменная - это Map<String, String> вместо 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 не выбран заранее на странице.

Это ожидаемое поведение компонента, или я что-то упускаю?

Заранее спасибо за помощь!

2 ответа

Прежде всего, noSelectionOption/ hideNoSelectionOptionПара атрибутов была неправильно понята здесь. Пожалуйста, удалите их. В вашем контексте это совершенно бесполезно. Чтобы лучше понять их первоначальную цель, перейдите к ответу Лучший способ добавить параметр «ничего не выбрано» в selectOneMenu в 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"поскольку это действительно слишком часто неправильно понимают. Вероятно, с ним следует обращаться так же, как с отключенным элементом.

Похоже, он пытается проверить значение и не может. Вы пытались вместо этого использовать собственный валидатор?

Другие вопросы по тегам