¿Cuándo usar valueChangeListener o f: ajax listener?

87

¿Cuál es la diferencia entre las siguientes dos piezas de código, con respecto a la listenerubicación?

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

y

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

Respuestas:

182

El valueChangeListenersólo se invoca cuando se envía el formulario y el valor presentado es diferente del valor inicial. Por lo tanto, no se invoca cuando solo se activa el changeevento DOM de HTML . Si desea enviar el formulario durante el changeevento DOM de HTML , deberá agregar otro <f:ajax/>sin un oyente (!) Al componente de entrada. Causará un envío de formulario que procesa solo el componente actual (como en execute="@this").

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

Cuando se usa en <f:ajax listener>lugar de valueChangeListener, ya se ejecutará de forma predeterminada durante el changeevento DOM de HTML . Dentro de los UICommandcomponentes y los componentes de entrada que representan una casilla de verificación o un botón de radio, se ejecutará de forma predeterminada solo durante el clickevento DOM de HTML .

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

Otra diferencia importante es que el valueChangeListenermétodo se invoca durante el final de la PROCESS_VALIDATIONSfase. En ese momento, el valor enviado aún no se ha actualizado en el modelo. Por lo tanto, no puede obtenerlo simplemente accediendo a la propiedad del bean que está vinculada al componente de entrada value. Tienes que conseguirlo ValueChangeEvent#getNewValue(). Por cierto, el valor antiguo también está disponible por ValueChangeEvent#getOldValue().

public void changeListener(ValueChangeEvent event) {
    Object oldValue = event.getOldValue();
    Object newValue = event.getNewValue();
    // ...
}

El <f:ajax listener>método se invoca durante la INVOKE_APPLICATIONfase. En ese momento, el valor enviado ya está actualizado en el modelo. Puede obtenerlo accediendo directamente a la propiedad del bean que está vinculada al componente de entrada value.

private Object value; // +getter+setter.

public void ajaxListener(AjaxBehaviorEvent event) {
    System.out.println(value); // Look, (new) value is already set.
}

Además, si necesita actualizar otra propiedad en función del valor enviado, fallará cuando esté utilizando, valueChangeListenerya que la propiedad actualizada puede ser anulada por el valor enviado durante la UPDATE_MODEL_VALUESfase siguiente . Esa es exactamente la razón por la que ve en las aplicaciones / tutoriales / recursos antiguos de JSF 1.x que valueChangeListenerse ha usado un en tal construcción en combinación con immediate="true"y FacesContext#renderResponse()para evitar que eso suceda. Después de todo, usando elvalueChangeListener para ejecutar acciones comerciales siempre ha sido un truco / solución alternativa.

Resumido: use el valueChangeListenersolo si necesita interceptar el cambio de valor real. Es decir, está realmente interesado tanto en el valor antiguo como en el nuevo (por ejemplo, para registrarlos).

public void changeListener(ValueChangeEvent event) {
    changeLogger.log(event.getOldValue(), event.getNewValue());
}

Use el <f:ajax listener>solo si necesita ejecutar una acción comercial sobre el valor recién cambiado. Es decir, en realidad está interesado solo en el nuevo valor (por ejemplo, para completar un segundo menú desplegable).

public void ajaxListener(AjaxBehaviorEvent event) {
    selectItemsOfSecondDropdown = populateItBasedOn(selectedValueOfFirstDropdown);
}

Si en realidad también está interesado en el valor anterior mientras ejecuta una acción comercial, retroceda valueChangeListener, pero colóquelo en la INVOKE_APPLICATIONfase.

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
fuente
@BalusC, ¿hay una razón para no obtener el viejo valor del objeto respaldo en la incubadora antes de que el nuevo valor que se pasa en? Algo como esto: logger.trace( "setting changeTypes from {} to {}", this.changeTypes, changeTypes );. Parece que podría usar los valores antiguos y nuevos obtenidos de esa manera para hacer la lógica comercial directamente en el establecedor, así como para un registro simple, pero no sé si esto causaría efectos secundarios ...
Lucas
¡Respuesta perfecta y completa! ¡Gracias por compartir su conocimiento!
hbobenicio
@BalusC ¿es posible obtener también el valor cambiado a través de AjaxBehaviorEvent? Tengo <h: selectManyListbox que está encadenado a otros componentes y me gustaría usar el modificado (agregado / eliminado) para realizar alguna acción
Paullo
Gracias @BalusC Pero no creo que ValueChangeListener se ajuste en mi caso, ya que tengo algunos valores de ejecución y representación como se muestra <f: ajax event = "valueChange" render = "tests" execute = "@ this" listener = "# {testController. processTimeTable} "/>
Paullo
9

para el primer fragmento (atributo de escucha ajax):

El atributo "oyente" de una etiqueta ajax es un método que se llama en el lado del servidor cada vez que ocurre la función ajax en el lado del cliente. Por ejemplo, puede usar este atributo para especificar una función del lado del servidor para llamar cada vez que el usuario presione una tecla

pero el segundo fragmento (valueChangeListener):

El ValueChangeListener solo se llamará cuando se envíe el formulario, no cuando se cambie el valor de la entrada

* es posible que desee ver esta útil respuesta

aur
fuente