Wann soll valueChangeListener oder f: ajax Listener verwendet werden?

86

Was ist der Unterschied zwischen den folgenden beiden Codeteilen - in Bezug auf die listenerPlatzierung?

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

und

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

Antworten:

182

Das valueChangeListenerwird nur aufgerufen, wenn das Formular gesendet wird und der übermittelte Wert vom Anfangswert abweicht. Es wird daher nicht aufgerufen, wenn nur das HTML-DOM- changeEreignis ausgelöst wird. Wenn Sie das Formular während des HTML-DOM- changeEreignisses senden möchten, müssen Sie <f:ajax/>der Eingabekomponente ein weiteres Formular ohne Listener (!) Hinzufügen . Es wird ein Formular gesendet, das nur die aktuelle Komponente verarbeitet (wie in execute="@this").

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

Bei Verwendung von <f:ajax listener>anstelle von valueChangeListenerwird es standardmäßig bereits während des HTML-DOM- changeEreignisses ausgeführt. Innerhalb von UICommandKomponenten und Eingabekomponenten, die ein Kontrollkästchen oder einen Radiobutton darstellen, wird dieser standardmäßig nur während des HTML-DOM- clickEreignisses ausgeführt.

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

Ein weiterer wesentlicher Unterschied besteht darin, dass die valueChangeListenerMethode am Ende der PROCESS_VALIDATIONSPhase aufgerufen wird. Zu diesem Zeitpunkt wurde der übermittelte Wert im Modell noch nicht aktualisiert. Sie können es also nicht erhalten, indem Sie einfach auf die Bean-Eigenschaft zugreifen, die an die Eingabekomponente gebunden ist value. Sie müssen es durch bekommen ValueChangeEvent#getNewValue(). Der alte Wert ist übrigens auch bei verfügbar ValueChangeEvent#getOldValue().

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

Die <f:ajax listener>Methode wird während der INVOKE_APPLICATIONPhase aufgerufen . Zu diesem Zeitpunkt wurde der übermittelte Wert bereits im Modell aktualisiert. Sie können es einfach erhalten, indem Sie direkt auf die Bean-Eigenschaft zugreifen, die an die Eingabekomponente gebunden ist value.

private Object value; // +getter+setter.

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

Wenn Sie eine andere Eigenschaft basierend auf dem übermittelten Wert valueChangeListeneraktualisieren müssten, schlägt dies bei der Verwendung fehl, da die aktualisierte Eigenschaft in der nachfolgenden Phase durch den übermittelten Wert überschrieben werden kannUPDATE_MODEL_VALUES . Das ist genau , warum Sie in alten JSF 1.x - Anwendungen / tutorials / Ressourcen sehen , dass ein valueChangeListenerin einem solchen Konstrukt in Kombination verwendet worden mit immediate="true"und FacesContext#renderResponse()um zu verhindern , dass aus geschieht. Schließlich war die Verwendung von valueChangeListenerzur Ausführung von Geschäftsaktionen eigentlich immer ein Hack / Workaround.

Zusammengefasst: Verwenden valueChangeListenerSie diese Option nur, wenn Sie die tatsächliche Wertänderung selbst abfangen müssen. Das heißt, Sie interessieren sich tatsächlich sowohl für den alten als auch für den neuen Wert (z. B. um sie zu protokollieren).

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

Verwenden <f:ajax listener>Sie diese Option nur, wenn Sie eine Geschäftsaktion für den neu geänderten Wert ausführen müssen. Das heißt, Sie interessieren sich tatsächlich nur für den neuen Wert (z. B. um ein zweites Dropdown-Menü auszufüllen).

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

Wenn Sie beim Ausführen einer Geschäftsaktion tatsächlich auch an dem alten Wert interessiert sind, greifen Sie auf zurück valueChangeListener, stellen Sie ihn jedoch in die Warteschlange für die INVOKE_APPLICATIONPhase.

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
quelle
@BalusC, gibt es einen Grund, den alten Wert nicht vom Hintergrundobjekt im Setter abzurufen, bevor er auf den neuen Wert gesetzt wird, der übergeben wird? So etwas wie das : logger.trace( "setting changeTypes from {} to {}", this.changeTypes, changeTypes );. Es scheint, als könnten Sie die alten und neuen Werte, die auf diese Weise erhalten wurden, verwenden, um Geschäftslogik direkt im Setter sowie einfache Protokollierung durchzuführen, aber ich weiß nicht, ob dies Nebenwirkungen verursachen würde ...
Lucas
Perfekte und vollständige Antwort! Vielen Dank für Ihr Wissen!
Hbobenicio
@BalusC ist es möglich, den geänderten Wert auch über AjaxBehaviorEvent abzurufen? Ich habe <h: selectManyListbox, die mit anderen Komponenten verkettet ist und die geänderte (Hinzugefügte / Entfernte) verwenden möchte, um eine Aktion auszuführen
Paullo
Danke @BalusC Aber ich glaube nicht, dass ValueChangeListener in meinen Fall passt, da ich einige Ausführungs- und Renderwerte wie gezeigt habe. <F: ajax event = "valueChange" render = "tests" execute = "@ this" listener = "# {testController. processTimeTable} "/>
Paullo
9

für das erste Fragment (Ajax-Listener-Attribut):

Das "Listener" -Attribut eines Ajax-Tags ist eine Methode, die auf der Serverseite jedes Mal aufgerufen wird, wenn die Ajax-Funktion auf der Clientseite ausgeführt wird. Mit diesem Attribut können Sie beispielsweise eine serverseitige Funktion angeben, die jedes Mal aufgerufen werden soll, wenn der Benutzer eine Taste drückt

aber das zweite Fragment (valueChangeListener):

Der ValueChangeListener wird nur aufgerufen, wenn das Formular gesendet wird, nicht, wenn der Wert der Eingabe geändert wird

* Vielleicht möchten Sie diese praktische Antwort anzeigen

aur
quelle