JSF SelectOneMenu показывает неправильное значение после исправления ошибки проверки в другом поле и повторной отправки формы - PullRequest
0 голосов
/ 29 сентября 2019

Я столкнулся с очень странной проблемой, когда элемент управления jSf selectOneMenu показывает неверное значение как выбранное (даже если модель обновляется с правильным значением) после того, как произошла ошибка проверки в совершенно другом поле предыдущего(не актуально) отправить.

Как тиражировать (минимальный код ниже):

  1. загрузить форму и оставить текстовое поле «Обязательное текстовое поле» пустым.
  2. Установите несколько значений «Активный» и «Неактивный» в паре раскрывающихся меню ниже.
  3. Отправьте форму. Появится сообщение об ошибке, в котором «Обязательное текстовое поле» будет пустым, а заданные раскрывающиеся списки по-прежнему будут иметь то же значение, которое вы им дали (ожидаемое). Распечатка того, что имеет модель, будет все еще показывать старое значение, а не значение, которое вы выбрали (ожидаемый).
  4. Поместите некоторый текст в «Обязательное текстовое поле» и отправьте форму.
  5. Сообщение об ошибке исчезает, и форма "сохраняется". Распечатки того, что модель имеет рядом с раскрывающимися списками, имеют заданное вами (ожидаемое) значение, однако сами раскрывающиеся списки больше не имеют значения, выбранного в качестве выбранного параметра (НЕ ОЖИДАЕМЫЙ).

enter image description here

Код образца:

    <?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
    <h:head>
        <title>Facelet Title</title>
    </h:head>
    <h:body>
        <h:form id="myForm">
            <div>
                <h:outputLabel value="Required text field" for="reqField" />
                <h:inputText value="#{myBeanController.requiredField}" id="reqField" label="Required Text Field" required="true" />
            </div>
            <div>
                <h:outputLabel value="some objects" />
                <ui:repeat value="#{myBeanController.bunchOfObjects}" var="obj">
                    <div>
                        value on obj model: #{obj}
                        <h:selectOneMenu value="#{obj.type}">
                            <f:selectItems value="#{myBeanController.availableRequiredTypeOptions}" />
                        </h:selectOneMenu>
                    </div>
                </ui:repeat>
            </div>
            <div>
                <h:commandButton value="Submit" action="#{myBeanController.save}" />
            </div>
        </h:form>
    </h:body>
</html>

Боб контроллера:

package com.mycompany.reproducefieldnotsavingaftervalidation;

import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import org.apache.commons.lang3.text.WordUtils;

@ManagedBean
@SessionScoped
public class MyBeanController {
    private String requiredField;
    private Collection<MyDomainClass> bunchOfObjects;

    @PostConstruct
    public void init(){
        // create some sample objects
        bunchOfObjects = new LinkedList<>();
        bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.ACTIVE));
        bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.INACTIVE));
        bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
        bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
        bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
        bunchOfObjects.add(new MyDomainClass(MyDomainClass.PossibleTypes.UNKNOWN));
    }

    public void save() {
        System.out.println("SAVING FORM");
        System.out.println("requiredField: " + getRequiredField());
        System.out.println("Bunch of objects: ");
        for(MyDomainClass obj : getBunchOfObjects()) {
            System.out.println("/tObj: " + obj);
        }
    }

    public Map<String, String> getAvailableRequiredTypeOptions() {
        Map<String, String> options = new TreeMap<>();

        for(MyDomainClass.PossibleTypes type : MyDomainClass.PossibleTypes.values()) {
            // make the text of the option pretty by removing all caps and replacing underscores with space
            options.put(WordUtils.capitalizeFully(type.name(), new char[]{'_'}).replaceAll("_", " "), type.name());
        }

        return options;
    }

    public String getRequiredField() {
        return requiredField;
    }

    public void setRequiredField(String requiredField) {
        this.requiredField = requiredField;
    }

    public Collection<MyDomainClass> getBunchOfObjects() {
        return bunchOfObjects;
    }

    public void setBunchOfObjects(Collection<MyDomainClass> bunchOfObjects) {
        this.bunchOfObjects = bunchOfObjects;
    }    
}

Модель:

package com.mycompany.reproducefieldnotsavingaftervalidation;

import java.util.Objects;

public class MyDomainClass {
    private String type;

    public MyDomainClass() { }

    public MyDomainClass(PossibleTypes type) {
        this.type = type.name();
    }

    public MyDomainClass(String type) {
        this.type = type;
    }

    public static enum PossibleTypes {
        UNKNOWN, ACTIVE, INACTIVE
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    @Override
    public String toString() {
        return "MyDomainClass{" + "type=" + type + '}';
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 79 * hash + Objects.hashCode(this.getType());
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof MyDomainClass)) {
            return false;
        }
        final MyDomainClass other = (MyDomainClass) obj;
        if (!Objects.equals(this.getType(), other.getType())) {
            return false;
        }
        return true;
    }

}

1 Ответ

0 голосов
/ 30 сентября 2019

Переключение

<ui:repeat> 

на

<c:forEach>

Кажется, это решило проблему. Я не знаю, почему пользовательский интерфейс: repeat вызывал проблемы только при сохранении после ранее неудачной проверки, а не при первом действительном сохранении (если кто-нибудь знает ответ на этот вопрос, пожалуйста, опубликуйте его, мне было бы интересно узнать). Основываясь на некоторых исследованиях, ui: repeat происходит на другой фазе, чем f: selectItem или f: selectItems, но тогда я ожидаю, что проблема будет возникать при каждом сохранении, и это не так.

В любом случае, если вы видите что-то похожее и ваши селекты находятся внутри пользовательского интерфейса: повторите, попробуйте переключить это с помощью ac: forEach, чтобы увидеть, исправляет ли это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...