Ответ 1
valueChangeListener
будет вызываться только при отправке формы и переданное значение отличается от начального значения. Таким образом, он не вызывается, когда запускается только событие HTML DOM change
. Если вы хотите отправить форму во время события HTML DOM change
, вам нужно добавить еще один <f:ajax/>
без прослушивателя (!) Во входной компонент. Это вызовет форму submit, которая обрабатывает только текущий компонент (как в execute="@this"
).
<h:selectOneMenu value="#{bean.value}" valueChangeListener="#{bean.changeListener}">
<f:selectItems ... />
<f:ajax />
</h:selectOneMenu>
При использовании <f:ajax listener>
вместо valueChangeListener
он по умолчанию будет выполнен во время события HTML DOM change
. Внутри UICommand
компонентов и входных компонентов, представляющих флажок или радиообъект, он будет по умолчанию выполняться только во время события HTML DOM click
.
<h:selectOneMenu value="#{bean.value}">
<f:selectItems ... />
<f:ajax listener="#{bean.ajaxListener}" />
</h:selectOneMenu>
Другое существенное отличие заключается в том, что метод valueChangeListener
вызывается в конце фазы PROCESS_VALIDATIONS
. В данный момент представленная ценность еще не обновлена в модели. Таким образом, вы не можете получить его, просто получив доступ к свойству bean, которое привязано к входному компоненту value
. Вы должны получить его ValueChangeEvent#getNewValue()
. Старое значение, кстати, также доступно ValueChangeEvent#getOldValue()
.
public void changeListener(ValueChangeEvent event) {
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
// ...
}
Метод <f:ajax listener>
вызывается во время фазы INVOKE_APPLICATION
. В этот момент представленная стоимость уже обновлена в модели. Вы можете просто получить его путем прямого доступа к свойству bean, которое привязано к входному компоненту value
.
private Object value; // +getter+setter.
public void ajaxListener(AjaxBehaviorEvent event) {
System.out.println(value); // Look, (new) value is already set.
}
Кроме того, если вам нужно будет обновить другое свойство на основе представленного значения, оно будет терпеть неудачу, если вы используете valueChangeListener
, поскольку обновленное свойство может быть переопределено представленным значением в течение следующей фазы UPDATE_MODEL_VALUES
. Именно поэтому вы видите в старых JSF 1.x приложениях/учебниках/ресурсах, что valueChangeListener
в такой конструкции используется в сочетании с immediate="true"
и FacesContext#renderResponse()
, чтобы это не происходило. В конце концов, использование valueChangeListener
для выполнения бизнес-операций всегда было хаком/обходным путем.
Подводя итог: используйте valueChangeListener
, только если вам нужно перехватить фактическое изменение значения. То есть вас действительно интересуют как старое, так и новое значение (например, для их регистрации).
public void changeListener(ValueChangeEvent event) {
changeLogger.log(event.getOldValue(), event.getNewValue());
}
Используйте <f:ajax listener>
только в том случае, если вам нужно выполнить бизнес-действие для вновь измененного значения. То есть вас действительно интересует только новое значение (например, для заполнения второго раскрывающегося списка).
public void ajaxListener(AjaxBehaviorEvent event) {
selectItemsOfSecondDropdown = populateItBasedOn(selectedValueOfFirstDropdown);
}
Если вы действительно заинтересованы в старом значении при выполнении бизнес-действия, вернитесь к valueChangeListener
, но оставьте очередь в фазе INVOKE_APPLICATION
.
public void changeListener(ValueChangeEvent event) {
if (event.getPhaseId() != PhaseId.INVOKE_APPLICATION) {
event.setPhaseId(PhaseId.INVOKE_APPLICATION);
event.queue();
return;
}
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
System.out.println(newValue.equals(value)); // true
// ...
}