Kiedy używać nasłuchiwania valueChangeListener lub f: ajax?

88

Jaka jest różnica między następującymi dwoma fragmentami kodu - w odniesieniu do listenerrozmieszczenia?

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

i

<h:selectOneMenu ... valueChangeListener="#{bean.listener}">
    <f:selectItems ... />
</h:selectOneMenu>
Danijel
źródło

Odpowiedzi:

183

valueChangeListenerZostanie wywołany tylko wtedy, gdy formularz jest składany i przedłożone wartość różni się od wartości początkowej. Dlatego nie jest wywoływana, gdy uruchamiane jest tylkochange zdarzenie HTML DOM . Jeśli chcesz wysłać formularz podczas changezdarzenia HTML DOM , musisz dodać kolejny <f:ajax/>bez listenera (!) Do komponentu wejściowego. Spowoduje to przesłanie formularza, który przetwarza tylko bieżący komponent (jak w execute="@this").

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

W przypadku użycia <f:ajax listener>zamiast valueChangeListener, byłoby to domyślnie wykonywane już podczas changezdarzenia HTML DOM . Wewnątrz UICommandkomponentów i komponentów wejściowych reprezentujących pole wyboru lub radiobutton byłby on domyślnie wykonywany tylko podczas clickzdarzenia HTML DOM .

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

Inną istotną różnicą jest to, że valueChangeListenermetoda jest wywoływana pod koniec PROCESS_VALIDATIONSfazy. W tym momencie podana wartość nie jest jeszcze aktualizowana w modelu. Więc nie możesz tego uzyskać, po prostu uzyskując dostęp do właściwości bean, która jest powiązana z komponentem wejściowym value. Musisz to zrobić ValueChangeEvent#getNewValue(). Nawiasem mówiąc, stara wartość jest również dostępna przez ValueChangeEvent#getOldValue().

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

<f:ajax listener>Sposób wywoływany jest w INVOKE_APPLICATIONfazie. W tym momencie przesłana wartość jest już zaktualizowana w modelu. Możesz go po prostu uzyskać, uzyskując bezpośredni dostęp do właściwości bean, która jest powiązana ze składnikiem wejściowym value.

private Object value; // +getter+setter.

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

Ponadto, jeśli musisz zaktualizować inną właściwość na podstawie przesłanej wartości, to zakończy się niepowodzeniem, gdy używasz valueChangeListenerzaktualizowanej właściwości, która może zostać zastąpiona przez przesłaną wartość w kolejnej UPDATE_MODEL_VALUESfazie. Właśnie dlatego widzisz w starych aplikacjach / samouczkach / zasobach JSF 1.x, że element a valueChangeListenerjest w takiej konstrukcji został użyty w połączeniu z immediate="true"i FacesContext#renderResponse()aby temu zapobiec. W końcu korzystanie z narzędzia valueChangeListenerdo wykonywania działań biznesowych zawsze było włamaniem / obejściem.

Podsumowanie: użyj valueChangeListenertylko wtedy, gdy musisz przechwycić samą zmianę wartości. Oznacza to, że jesteś zainteresowany zarówno starą, jak i nową wartością (np. Aby je zarejestrować).

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

Użyj <f:ajax listener>tylko, jeśli musisz wykonać akcję biznesową na nowo zmienionej wartości. Oznacza to, że interesuje Cię tylko nowa wartość (np. Aby wypełnić drugie menu).

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

Jeśli faktycznie interesuje Cię stara wartość podczas wykonywania czynności biznesowej, wróć do valueChangeListener, ale ustaw ją w kolejce do INVOKE_APPLICATIONfazy.

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
źródło
@BalusC, czy istnieje powód, aby nie uzyskiwać starej wartości z obiektu zapasowego w programie ustawiającym przed ustawieniem jej na nową wartość, która jest przekazywana? Coś takiego: logger.trace( "setting changeTypes from {} to {}", this.changeTypes, changeTypes );. Wygląda na to, że można by wykorzystać stare i nowe wartości uzyskane w ten sposób do wykonywania logiki biznesowej bezpośrednio w ustawiaczu, a także do prostego logowania, ale nie wiem, czy to spowodowałoby skutki uboczne ...
Lucas
Doskonała i pełna odpowiedź! Dziękujemy za podzielenie się wiedzą!
hbobenicio
@BalusC Czy można również uzyskać zmienioną wartość za pomocą AjaxBehaviorEvent? Mam <h: selectManyListbox, które jest powiązane z innymi komponentami i chciałbym użyć zmienionych (dodanych / usuniętych) do wykonania jakiejś czynności
Paullo
Dzięki @BalusC Ale nie sądzę, że ValueChangeListener będzie pasował do mojego przypadku, ponieważ mam pewne wartości wykonania i renderowania, jak pokazano <f: ajax event = "valueChange" render = "testy" execute = "@ this" listener = "# {testController. processTimeTable} "/>
Paullo,
9

dla pierwszego fragmentu (atrybut AJAX listener):

Atrybut „listener” tagu Ajax jest metodą wywoływaną po stronie serwera za każdym razem, gdy funkcja AJAX ma miejsce po stronie klienta. Na przykład, możesz użyć tego atrybutu, aby określić funkcję po stronie serwera, która ma być wywoływana za każdym razem, gdy użytkownik naciśnie klawisz

ale drugi fragment (valueChangeListener):

Element ValueChangeListener zostanie wywołany tylko po przesłaniu formularza, a nie po zmianie wartości danych wejściowych

* możesz chcieć zobaczyć tę przydatną odpowiedź

aur
źródło