Когда использовать valueChangeListener или f: ajax listener?

88

В чем разница между двумя следующими фрагментами кода - в отношении listenerразмещения?

<h:selectOneMenu ...>
    <f:selectItems ... />
    <f:ajax listener="#{bean.listener}" />
</h:selectOneMenu>

а также

<h:selectOneMenu ... valueChangeListener="#{bean.listener}">
    <f:selectItems ... />
</h:selectOneMenu>
Даниэль
источник

Ответы:

183

Объект valueChangeListenerбудет вызываться только тогда, когда форма отправлена и отправленное значение отличается от начального значения. Таким образом, он не вызывается, когда запускается толькоchange событие HTML DOM . Если вы хотите отправить форму во время changeсобытия HTML DOM , вам нужно будет добавить другую <f:ajax/>форму без слушателя (!) К компоненту ввода. Это вызовет отправку формы, которая обрабатывает только текущий компонент (как в execute="@this").

<h:selectOneMenu value="#{bean.value}" valueChangeListener="#{bean.changeListener}">
    <f:selectItems ... />
    <f:ajax />
</h:selectOneMenu>

При использовании <f:ajax listener>вместо valueChangeListener, он по умолчанию будет выполняться уже во время changeсобытия HTML DOM . Внутри UICommandкомпонентов и компонентов ввода, представляющих флажок или радиокнопку, по умолчанию он будет выполняться только во время clickсобытия HTML DOM .

<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
    // ...
}
BalusC
источник
@BalusC, есть ли причина не получать старое значение из объекта поддержки в установщике, прежде чем устанавливать его на новое значение, которое передается? Что - то вроде этого: logger.trace( "setting changeTypes from {} to {}", this.changeTypes, changeTypes );. Похоже, вы могли бы использовать старые и новые значения, полученные таким образом, для выполнения бизнес-логики непосредственно в установщике, а также для простого ведения журнала, но я не знаю, вызовет ли это побочные эффекты ...
Лукас,
Прекрасный и полный ответ! Спасибо, что поделились своими знаниями!
hbobenicio
@BalusC, возможно ли также получить измененное значение через AjaxBehaviorEvent? У меня есть <h: selectManyListbox, который привязан к другим компонентам, и я хотел бы использовать измененный (добавленный / удаленный) для выполнения некоторых действий
Полло
Спасибо @BalusC. Но я не думаю, что ValueChangeListener подойдет в моем случае, поскольку у меня есть несколько значений выполнения и рендеринга, как показано <f: ajax event = "valueChange" render = "tests" execute = "@ this" listener = "# {testController. processTimeTable} "/>
Paullo
9

для первого фрагмента (атрибут прослушивателя ajax):

Атрибут «слушателя» тега ajax - это метод, который вызывается на стороне сервера каждый раз, когда функция ajax выполняется на стороне клиента. Например, вы можете использовать этот атрибут, чтобы указать функцию на стороне сервера, вызываемую каждый раз, когда пользователь нажимает клавишу.

но второй фрагмент (valueChangeListener):

ValueChangeListener будет вызываться только при отправке формы, а не при изменении значения ввода.

* вы можете посмотреть этот удобный ответ

aur
источник