JSF — значение недопустимо по неизвестной причине ›› Переоценить «дубликат»

В моем приложении JSF, использующем RichFaces, у меня есть экран с rich:dataTable, который отображает мои объекты, и столбец, который ссылается на другую страницу, с подробностями выбранной записи, чтобы ее можно было использовать.

На этой второй странице после заполнения запрошенных данных при запуске submit проверка возвращает сообщение:

:Value is not a valid option.

Обратите внимание, что, в отличие от проблемы, упомянутой в этом сообщении, название проблемы поле не отображается.

Во время отладки я заметил, что в методе selecionarEmitente() объект localidade имеет значение null. Если я сделал набор на странице dataTable, а имя выбранного объекта появилось на второй странице, чего не хватает?

Я уже исследовал другие сообщения об этой проблеме. Например, согласно этой публикации, мои задействованные javabeans имеют методы equals() и hashCode(). В отличие от этого другого сообщения, я с помощью пользовательских конвертеров.

Где моя ошибка?

Я использую RichFaces 4.5.1.Final, MyFaces 2.2.10, Spring 3.1.1.

dataTable.xhtml

<h:selectOneMenu id="estado"
    immediate="true" validator="#{validadorMB.validarEstado}"
    converter="estadoConverter">
    <f:selectItem itemLabel="---" />
    <f:selectItems value="#{localidadeMB.estados}"
                   var="est" itemValue="#{est}" itemLabel="#{est.uf}" />
    <a4j:ajax event="change" actionListener="#{localidadeMB.filtrarUF}" render="table" />
</h:selectOneMenu>

<a4j:region id="region" immediate="true">
    <rich:dataTable id="tabela"
        value="#{localidadeMB.getLocalidadesPorUF()}" var="loc"
        rowKeyVar="row" rows="20" width="800px" render="scroller">
        <rich:column id="col_localidade" sortBy="#{loc.nome}" filterBy="#{loc.nome}">
            <h:outputText id="nomeLocalidade" value="#{loc.nome}" />
        </rich:column>
        ...
        <rich:column>
            <h:commandLink id="declaracaolink" action="#{localidadeMB.carregarLocalidade}">
                <h:graphicImage url="/img/declaracao.png" />
                <f:setPropertyActionListener value="#{loc}" target="#{localidadeMB.localidade}" />
            </h:commandLink>
        </rich:column>
    </rich:dataTable>
</a4j:region>

вторая страница.xhtml

<h:body>
    <h:form id="formDeclaracao" focus="estado">
        <h:panelGrid columns="2" columnClasses="labelFormulario">
            <h:outputLabel value="UF" for="estado" />
            <h:selectOneMenu id="estado"
                value="#{localidadeMB.localidade.estado}"
                label="#{localidadeMB.localidade.estado.uf}"
                valueChangeListener="#{localidadeMB.selecionarEstado}"
                validator="#{validadorMB.validarEstado}" immediate="true"
                converter="estadoConverter"
                style="width: 45px;" styleClass="listbox">
                <f:selectItems value="#{localidadeMB.estados}"
                       var="est" itemValue="#{est}" itemLabel="#{est.uf}" />
                <a4j:ajax event="change" render="nomeLocalidade" />
            </h:selectOneMenu>

            <h:outputLabel value="Nome da localidade:" for="nomeLocalidade" />
            <h:selectOneMenu id="nomeLocalidade"
                value="#{localidadeMB.localidade}"
                label="#{localidadeMB.localidade.nome}" immediate="true"
                valueChangeListener="#{localidadeMB.selecionarLocalidade}"
                validator="#{validadorMB.validarLocalidade}"
                converter="localidadeConverter"
                style="width: 220px;" styleClass="listbox">
                <f:selectItems value="#{localidadeMB.localidades}"
                     var="loc" itemValue="#{loc}" itemLabel="#{loc.nome}" />
            </h:selectOneMenu>

            <h:outputLabel value="Interessado:" for="interessado" />
            <h:inputText id="interessado" value="" required="true"
                requiredMessage="xxxxxxxxx" immediate="true"
                styleClass="listbox" style="width: 215px;">
            </h:inputText>

            <h:outputLabel value="Solicitante:" for="solicitante" />
            <h:inputText id="solicitante" value="" required="true"
                requiredMessage="xxxxxxxxxxxxx" immediate="true">
            </h:inputText>

            <h:outputLabel value="Documento apresentado:" for="documento" />
            <h:inputText id="documento" value="" required="true"
                requiredMessage="xxxxxxxxxxxxx" immediate="true">
            </h:inputText>

            <h:outputLabel value="Emitente:" for="emitente" />
            <h:selectOneMenu id="emitente"
                value="#{localidadeMB.emitente}"
                label="#{localidadeMB.emitente.descricaoReduzida}"
                valueChangeListener="#{localidadeMB.selecionarEmitente}"
                validator="#{validadorMB.validarEmitente}"
                converter="emitenteConverter">
                <f:selectItem itemLabel="---" />
                <f:selectItems value="#{localidadeMB.emitentes}" var="em" itemValue="#{em}" itemLabel="#{em.descricaoReduzida}" />
            </h:selectOneMenu>

            <h:commandButton id="btnGerarDeclaracao" value="Get PDF"
                action="#{localidadeMB.getPDF()}" style="width: 84px;" />
        </h:panelGrid>
    </h:form>
</h:body>

Управляемый компонент

@ManagedBean
public class LocalidadeMB{
    private Emitente emitente;
    private Estado estado;
    private Localidade localidade;

    public String carregarLocalidade() {
        estado = localidade.getEstado();
        localidade = getLocalidade();
        getLocalidades();
        carregarLocalidadePorUF(estado);
        return "secondPage.xhtml";
    }

    public void filtrarUF(ActionEvent action) {
        try {
            String uf = JSFHelper.getRequestParameter("formConsulta"
                + UINamingContainer.getSeparatorChar(JSFHelper
                    .getFacesContext()) + "estado");
            estado = estadoFacade.getEstado(Integer.parseInt(uf));
        } catch (Exception e) {
            ...
        }
    }

    public List<Emitente> getEmitentes() {
        try {
            return emitenteFacade.getEmitentes();
        } catch (Exception e) {
            ...
        }
    }

    public List<Emitente> getEstados() {
        try {
            return estadoFacade.getEstados();
        } catch (...) {
           ...
        }
    }

    public List<Localidade> getLocalidades() {
        try {
            return localidadeFacade.getLocalidades();
        } catch (Exception e) {
            ...
        }
    }

    public List<Localidade> getLocalidadesPorUF() {
        try {
            String uf = JSFHelper.getRequestParameter("formConsulta"
                + UINamingContainer.getSeparatorChar(JSFHelper
                        .getFacesContext()) + "estado");
            if ((uf != null) && (!uf.equals("---"))) {
                estado = estadoFacade.getEstado(Integer.parseInt(uf));
                return localidadeFacade.getLocalidadesPorEstado(estado);
            } else {
                return null;
            }
        } catch (Exception e) {
            ...
        }
    }


    public void selecionarEmitente(ValueChangeEvent evento) {
        emitente = (Emitente)evento.getNewValue();
    }

    public void selecionarEstado(ValueChangeEvent evento) {
        estado = (Estado)evento.getNewValue();
    }

    public void selecionarLocalidade(ValueChangeEvent evento) {
        localidade = (Localidade)evento.getNewValue();
    }
}

Валидатор

@ManagedBean(name = "validadorMB")
public class ValidadorLocalidadeMB {
    public void validarEmitente(FacesContext context, UIComponent componentToValidate, Object value) throws ValidatorException {
        if (((Emitente) value).getEmitenteId() == 0) {  
            FacesMessage msg = new FacesMessage(null, "Selecione o emitente");
            throw new ValidatorException(msg);
        }
    }

    public void validarEstado(FacesContext context, UIComponent componentToValidate, Object value) throws ValidatorException {
        if (((Estado) value).getEstadoId() == 0) {  
            FacesMessage msg = new FacesMessage(null, "Selecione uma UF");
            throw new ValidatorException(msg);
        }
    }

    public void validarLocalidade(FacesContext context, UIComponent componentToValidate, Object value) throws ValidatorException {
        if (((Localidade) value).getLocalidadeId() == 0) {  
            FacesMessage msg = new FacesMessage(null, "Selecione uma localidade");
            throw new ValidatorException(msg);
        }
    }
}

Конвертеры

@FacesConverter(value = "emitenteConverter")
public class EmitenteConverter implements Converter {

    @Override
    public Object getAsObject(FacesContext context, UIComponent ui, String value) throws ConverterException {
        ValueExpression vex = context
            .getApplication()
            .getExpressionFactory()
            .createValueExpression(context.getELContext(),
                    "#{emitenteFacade}", EmitenteFacadeImpl.class);

        EmitenteFacadeImpl fac = (EmitenteFacadeImpl) vex.getValue(context
            .getELContext());
        try {
            return fac.getEmitentePorId(Integer.valueOf(value));
        } catch (NumberFormatException | DAOException e) {
         ....
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent ui, Object value) throws ConverterException {
        if (value == null) {
            return "";
        }

        if (value instanceof Emitente) {
            return String.valueOf(((Emitente) value).getEmitenteId());
        }
    }
}

@FacesConverter(value = "estadoConverter")
public class EstadoConverter implements Converter {

    @Override
    public Object getAsObject(FacesContext context, UIComponent ui, String value) throws ConverterException {
        ValueExpression vex = context
            .getApplication()
            .getExpressionFactory()
            .createValueExpression(context.getELContext(),
                    "#{estadoFacade}", EstadoFacadeImpl.class);

        EstadoFacadeImpl fac = (EstadoFacadeImpl) vex.getValue(context
            .getELContext());
        try {
            return fac.getEstadoPorId(Integer.valueOf(value));
        } catch (NumberFormatException | DAOException e) {
         ....
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent ui, Object value) throws ConverterException {
        if (value == null) {
            return "";
        }

        if (value instanceof Estado) {
            return String.valueOf(((Estado) value).getEstadoId());
        }
    }
}

@FacesConverter(value = "localidadeConverter")
public class LocalidadeConverter implements Converter {

    @Override
    public Object getAsObject(FacesContext context, UIComponent ui, String value) throws ConverterException {
        ValueExpression vex = context
            .getApplication()
            .getExpressionFactory()
            .createValueExpression(context.getELContext(),
                    "#{localidadeFacade}", LocalidadeFacadeImpl.class);

        LocalidadeFacadeImpl fac = (LocalidadeFacadeImpl) vex.getValue(context.getELContext());
        try {
            return fac.getLocalidadePorId(Integer.valueOf(value));
        } catch (NumberFormatException | DAOException e) {
         ....
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent ui, Object value) throws ConverterException {
        if (value == null) {
            return "";
        }

        if (value instanceof Localidade) {
            return String.valueOf(((Localidade) value).getLocalidadeId());
        }
    }
}

person Rogério Arantes    schedule 20.01.2017    source источник
comment
Можете ли вы сузить этот вопрос до одного компонента?   -  person Emil Sierżęga    schedule 21.01.2017
comment
У вас не должно быть никакой бизнес-логики в геттерах, хотя трудно сказать, является ли это проблемой.   -  person Makhiel    schedule 23.01.2017
comment
Извините, Эмиль Сержега, я не понял вашего вопроса.   -  person Rogério Arantes    schedule 23.01.2017
comment
Makhiel, я лично не верю, что проблема в геттере, так как он только конструирует элементы SELECT. Настолько, что выбор отлично отображается в HTML.   -  person Rogério Arantes    schedule 23.01.2017
comment
Выбранное значение сравнивается с выбранными элементами, вот откуда вы получаете ошибку. Помимо того, что геттер вызывается несколько раз, он может не возвращать одни и те же значения.   -  person Makhiel    schedule 24.01.2017
comment
BalusC, вы пометили это сообщение как повторяющееся, но в отличие от сообщения, на которое вы ссылаетесь, в моей проблеме сообщение об ошибке не отображает компонент, в котором ошибка   -  person Rogério Arantes    schedule 31.01.2017


Ответы (1)


Ваша проблема может быть с этой строкой:

value="#{(localidadeMB.localidade.localidadeId != null) ? localidadeMB.localidade.localidadeId :localidadeMB.localidade}"

Значение <h:selectOneMenu> должно быть простым свойством, а не выражением, поскольку оно должно быть установлено.


Редактировать:

Может быть другая проблема с вашим кодом:

Вы создаете эти SelectItem с идентификатором объекта в качестве свойства value, а затем используете конвертер для преобразования String в/из самого объекта.

Таким образом, у вас есть несколько возможностей:

  1. Используйте преобразователь и «целый» объект как value из SelectItem.
  2. Или не используйте Selectitem, а List самих сущностей, как ваш первой ссылки.
  3. Другая возможность — использовать фиктивный объект, установить его свойство id и в вашем valueChangeListener получить объект по идентификатору.
person Usagi Miyamoto    schedule 20.01.2017
comment
Спасибо за ваш ответ. Я не думаю, что есть это ограничение, но по вашему предложению я удалил выражение и оставил простое значение (localidadeMB.localidade.localidadeId), но это не сработало. - person Rogério Arantes; 23.01.2017
comment
Атрибут value действительно неверен, но это будет проявляться как проблема только на этапе обновления значений модели именно с этой ошибкой: javax.el.PropertyNotWritableException: Illegal Syntax for Set Operation. Однако конкретная проблема OP уже возникает на этапе проверки и привела к совершенно другой ошибке. - person BalusC; 24.01.2017
comment
Я изменил код в соответствии с предложениями, но ошибка остается. Я изменил код и вставил выдержки с другой страницы, чтобы можно было понять контекст. - person Rogério Arantes; 31.01.2017