Когда мы разрабатываем веб-приложения с использованием JavaServer Faces (JSF), нам часто приходится разбираться с неожиданными нюансами поведения jsf-компонентов.
Сегодня поговорим о следующей проблеме с selectOneMenu
: выбранное значение выпадающего списка отображается пустым, даже если в базе данных есть все необходимые данные, а в поле бина выбрано корректное значение.
Пример
Рассмотрим пример выпадающего списка, где пользователь выбирает исполнителя задачи:
<p:selectOneMenu id="executor" value="#{taskBean.currentTask.executor}"
converter="#{taskBean.executorConverter}" effect="fade"
required="true" requiredMessage="Пожалуйста, выберите исполнителя">
<p:ajax event="change" process="@this" update="@this"/>
<f:selectItem itemLabel="-- Выберите исполнителя --" itemValue="#{null}"/>
<f:selectItems value="#{taskBean.executors}" var="executor"
itemLabel="#{executor.name}" itemValue="#{executor}"/>
</p:selectOneMenu>
В этом коде мы отображаем список исполнителей задач, загружаемых из базы данных. При загрузке страницы компонент должен установить текущего исполнителя, выбранного ранее. Однако иногда оно оказывается пустым, хотя данные о текущем исполнителе есть, и выбранное значение не null
.
Причина проблемы
Проблема заключается в том, что JSF, когда рендерит компонент selectOneMenu
, использует метод equals
для сравнения объектов. Если у объекта, который хранится в модели (currentTask.executor
), и объекта, который пришел из списка (executors
), методы equals
и hashCode
не реализованы корректно, то даже при идентичных данных JSF не сможет их сопоставить. В результате компонент посчитает, что выбранный элемент отсутствует, и отобразит пустое значение.
Мораль: всегда переопределяйте equals
и hashCode
для объектов, которые могут быть использованы в UI, сгенерированном с помощью JSF-компонентов.