Quand utiliser valueChangeListener ou f: ajax listener?

88

Quelle est la différence entre les deux morceaux de code suivants - en ce qui concerne le listenerplacement?

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

et

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

Réponses:

183

Le valueChangeListenerne sera invoqué que lorsque le formulaire est soumis et que la valeur soumise est différente de la valeur initiale. Il n'est donc pas appelé lorsque seul l' changeévénement HTML DOM est déclenché. Si vous souhaitez soumettre le formulaire pendant l' changeévénement HTML DOM , vous devez en ajouter un autre <f:ajax/>sans écouteur (!) Au composant d'entrée. Cela entraînera une soumission de formulaire qui ne traite que le composant actuel (comme dans execute="@this").

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

Lors de l'utilisation <f:ajax listener>au lieu de valueChangeListener, il serait déjà exécuté par défaut lors de l' changeévénement HTML DOM . À l'intérieur des UICommandcomposants et des composants d'entrée représentant une case à cocher ou un radiobutton, il serait par défaut exécuté pendant l' clickévénement HTML DOM uniquement.

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

Une autre différence majeure est que la valueChangeListenerméthode est appelée à la fin de la PROCESS_VALIDATIONSphase. À ce moment, la valeur soumise n'est pas encore mise à jour dans le modèle. Vous ne pouvez donc pas l'obtenir en accédant simplement à la propriété du bean qui est liée au composant d'entrée value. Vous devez y arriver ValueChangeEvent#getNewValue(). L'ancienne valeur est d'ailleurs également disponible par ValueChangeEvent#getOldValue().

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

La <f:ajax listener>méthode est appelée pendant la INVOKE_APPLICATIONphase. À ce moment, la valeur soumise est déjà mise à jour dans le modèle. Vous pouvez simplement l'obtenir en accédant directement à la propriété du bean qui est liée au composant d'entrée value.

private Object value; // +getter+setter.

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

De plus, si vous devez mettre à jour une autre propriété en fonction de la valeur soumise, elle échouera lorsque vous l'utilisez, valueChangeListenercar la propriété mise à jour peut être remplacée par la valeur soumise lors de la UPDATE_MODEL_VALUESphase suivante . C'est exactement pourquoi vous voyez dans les anciennes applications / tutoriels / ressources JSF 1.x qu'un valueChangeListenerest dans une telle construction a été utilisé en combinaison avec immediate="true"et FacesContext#renderResponse()pour empêcher que cela se produise. Après tout, l'utilisation de valueChangeListenerpour exécuter des actions commerciales a toujours été un hack / une solution de contournement.

Résumé: utilisez le valueChangeListeneruniquement si vous devez intercepter le changement de valeur réelle lui-même. -À- dire que vous êtes réellement intéressé à la fois l'ancienne et la nouvelle valeur (par exemple pour les log).

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

Utilisez le <f:ajax listener>uniquement si vous devez exécuter une action commerciale sur la valeur nouvellement modifiée. C'est-à-dire que vous n'êtes réellement intéressé que par la nouvelle valeur (par exemple pour remplir une deuxième liste déroulante).

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

Si vous êtes également intéressé par l'ancienne valeur lors de l'exécution d'une action commerciale, revenez valueChangeListenerà la INVOKE_APPLICATIONphase , mais mettez-la en file d'attente .

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
la source
@BalusC, y a-t-il une raison de ne pas obtenir l' ancienne valeur de l'objet de support dans le setter avant de le définir sur la nouvelle valeur transmise? Quelque chose comme ceci: logger.trace( "setting changeTypes from {} to {}", this.changeTypes, changeTypes );. Il semble que vous puissiez utiliser les anciennes et les nouvelles valeurs obtenues de cette manière pour faire de la logique métier directement dans le setter ainsi qu'une simple journalisation, mais je ne sais pas si cela provoquerait des effets secondaires ...
Lucas
Réponse parfaite et complète! Merci de partager votre savoir!
hbobenicio
@BalusC est-il possible d'obtenir également la valeur modifiée via AjaxBehaviorEvent? J'ai <h: selectManyListbox qui est enchaîné à d'autres composants et j'aimerais utiliser le modifié (ajouté / supprimé) pour effectuer une action
Paullo
Merci @BalusC Mais je ne pense pas que ValueChangeListener conviendra à mon cas car j'ai des valeurs d'exécution et de rendu comme indiqué <f: ajax event = "valueChange" render = "tests" execute = "@ this" listener = "# {testController. processTimeTable} "/>
Paullo
9

pour le premier fragment (attribut d'écoute ajax):

L'attribut "listener" d'une balise ajax est une méthode qui est appelée côté serveur à chaque fois que la fonction ajax se produit côté client. Par exemple, vous pouvez utiliser cet attribut pour spécifier une fonction côté serveur à appeler chaque fois que l'utilisateur appuie sur une touche

mais le deuxième fragment (valueChangeListener):

Le ValueChangeListener ne sera appelé que lorsque le formulaire est soumis, pas lorsque la valeur de l'entrée est modifiée

* vous aimerez peut-être voir cette réponse pratique

aur
la source