Quelle est la différence entre action
et actionListener
et quand dois-je utiliser action
versus actionListener
?
Quelle est la différence entre action
et actionListener
et quand dois-je utiliser action
versus actionListener
?
Réponses:
A utiliser actionListener
si vous voulez avoir un hook avant l'exécution de la véritable action métier, par exemple pour la journaliser, et / ou pour définir une propriété supplémentaire (par <f:setPropertyActionListener>
), et / ou pour avoir accès au composant qui a appelé l'action (qui est disponible parActionEvent
argument). Donc, uniquement pour préparer des fins avant que la véritable action commerciale ne soit invoquée.
La actionListener
méthode a par défaut la signature suivante:
import javax.faces.event.ActionEvent;
// ...
public void actionListener(ActionEvent event) {
// ...
}
Et il est censé être déclaré comme suit, sans aucune parenthèse de méthode:
<h:commandXxx ... actionListener="#{bean.actionListener}" />
Notez que vous ne pouvez pas passer d' arguments supplémentaires par EL 2.2. Vous pouvez cependant remplacer ActionEvent
complètement l' argument en passant et en spécifiant des arguments personnalisés. Les exemples suivants sont valides:
<h:commandXxx ... actionListener="#{bean.methodWithoutArguments()}" />
<h:commandXxx ... actionListener="#{bean.methodWithOneArgument(arg1)}" />
<h:commandXxx ... actionListener="#{bean.methodWithTwoArguments(arg1, arg2)}" />
public void methodWithoutArguments() {}
public void methodWithOneArgument(Object arg1) {}
public void methodWithTwoArguments(Object arg1, Object arg2) {}
Notez l'importance des parenthèses dans l'expression de la méthode sans argument. S'ils étaient absents, JSF s’attendrait toujours à une méthodeActionEvent
argument.
Si vous utilisez EL 2.2+, vous pouvez déclarer plusieurs méthodes d'écoute d'actions via <f:actionListener binding>
.
<h:commandXxx ... actionListener="#{bean.actionListener1}">
<f:actionListener binding="#{bean.actionListener2()}" />
<f:actionListener binding="#{bean.actionListener3()}" />
</h:commandXxx>
public void actionListener1(ActionEvent event) {}
public void actionListener2() {}
public void actionListener3() {}
Notez l'importance des parenthèses dans l' binding
attribut. S'ils étaient absents, EL lancerait une confusion javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean
, car l' binding
attribut est interprété par défaut comme une expression de valeur, pas comme une expression de méthode. L'ajout de parenthèses de style EL 2.2+ transforme de manière transparente une expression de valeur en une expression de méthode. Voir aussi ao Pourquoi suis-je capable de lier <f: actionListener> à une méthode arbitraire si elle n'est pas prise en charge par JSF?
À utiliser action
si vous souhaitez exécuter une opération commerciale et, si nécessaire, gérer la navigation. La action
méthode peut (donc, ne doit pas) retourner un String
qui sera utilisé comme résultat du cas de navigation (la vue cible). Une valeur de retour denull
ou la void
laissera revenir à la même page et conserver la portée de la vue actuelle. Une valeur de retour d'une chaîne vide ou du même ID de vue retournera également à la même page, mais recréera l'étendue de la vue et détruira ainsi tous les beans de portée de vue actuellement actifs et, le cas échéant, les recréera.
La action
méthode peut être toute valide MethodExpression
, également celles qui utilisent des arguments EL 2.2 comme ci-dessous:
<h:commandXxx value="submit" action="#{bean.edit(item)}" />
Avec cette méthode:
public void edit(Item item) {
// ...
}
Notez que lorsque votre méthode d'action renvoie uniquement une chaîne, vous pouvez également spécifier exactement cette chaîne dans l' action
attribut. C'est donc totalement maladroit:
<h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />
Avec cette méthode insensée renvoyant une chaîne codée en dur:
public String goToNextpage() {
return "nextpage";
}
Au lieu de cela, placez simplement cette chaîne codée en dur directement dans l'attribut:
<h:commandLink value="Go to next page" action="nextpage" />
Veuillez noter que cela indique à son tour une mauvaise conception: naviguer par POST. Ce n'est ni convivial ni SEO. Tout cela est expliqué dans Quand dois-je utiliser h: outputLink au lieu de h: commandLink? et est censé être résolu comme
<h:link value="Go to next page" outcome="nextpage" />
Voir aussi Comment naviguer dans JSF? Comment faire en sorte que l'URL reflète la page actuelle (et non la précédente) .
Depuis JSF 2.x, il existe une troisième voie, la <f:ajax listener>
.
<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
La ajaxListener
méthode a par défaut la signature suivante:
import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
Dans Mojarra, l' AjaxBehaviorEvent
argument est facultatif, ci-dessous fonctionne aussi bien.
public void ajaxListener() {
// ...
}
Mais dans MyFaces, cela lancerait un MethodNotFoundException
. Ci-dessous fonctionne dans les deux implémentations JSF lorsque vous souhaitez omettre l'argument.
<h:commandXxx ...>
<f:ajax execute="@form" listener="#{bean.ajaxListener()}" render="@form" />
</h:commandXxx>
Les écouteurs Ajax ne sont pas vraiment utiles sur les composants de commande. Ils sont plus utiles en entrée et en sélectionnant les composants <h:inputXxx>
/ <h:selectXxx>
. Dans les composants de commande, respectez simplement action
et / ou actionListener
pour plus de clarté et un meilleur code d'auto-documentation. De plus, comme actionListener
, le f:ajax listener
ne prend pas en charge le retour d'un résultat de navigation.
<h:commandXxx ... action="#{bean.action}">
<f:ajax execute="@form" render="@form" />
</h:commandXxx>
Pour des explications sur execute
et les render
attributs, rendez-vous dans Comprendre le processus / mise à jour PrimeFaces et JSF f: ajax exécuter / rendre les attributs .
Les actionListener
s sont toujours appelés avant le action
dans le même ordre qu'ils ont été déclarés dans la vue et attachés au composant. Le f:ajax listener
est toujours invoqué avant tout écouteur d'action. Ainsi, l'exemple suivant:
<h:commandButton value="submit" actionListener="#{bean.actionListener}" action="#{bean.action}">
<f:actionListener type="com.example.ActionListenerType" />
<f:actionListener binding="#{bean.actionListenerBinding()}" />
<f:setPropertyActionListener target="#{bean.property}" value="some" />
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandButton>
Appellera les méthodes dans l'ordre suivant:
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
Le actionListener
prend en charge une exception spéciale:AbortProcessingException
. Si cette exception est levée à partir d'une actionListener
méthode, alors JSF ignorera tous les écouteurs d'action restants et la méthode d'action et procédera au rendu de la réponse directement. Vous ne verrez pas de page d'erreur / d'exception, JSF l'enregistrera cependant. Cela se fera également implicitement chaque fois qu'une autre exception est levée à partir d'un actionListener
. Donc, si vous avez l'intention de bloquer la page par une page d'erreur à la suite d'une exception commerciale, vous devez certainement effectuer le travail dans la action
méthode.
Si la seule raison d'utiliser un actionListener
est d'avoir une void
méthode retournant à la même page, alors c'est une mauvaise. Les action
méthodes peuvent également parfaitement retourner void
, contrairement à ce que certains IDE vous laissent croire via la validation EL. Notez que le exemples de vitrines PrimeFaces sont jonchés de ce type deactionListener
s partout. C'est vraiment faux. N'utilisez pas cela comme excuse pour le faire vous-même.
Dans les requêtes ajax, cependant, un gestionnaire d'exceptions spécial est nécessaire. Cela indépendamment du fait que vous listener
utilisiez <f:ajax>
ou non l' attribut . Pour obtenir des explications et un exemple, consultez la section Gestion des exceptions dans les demandes ajax JSF .
actionListener
, mais cela ne constitue toujours pas une bonne excuse pour abuser actionListener
d' actions commerciales .
action
correspond à cela. actionListener
est pour des trucs secondaires. Je voulais juste préciser que les exceptions de actionListener
s peuvent être propagées si nécessaire;)
actionListener
attribut et il doit l'être public
également. Le processAction
nom n'est obligatoire que lorsque vous utilisez <f:actionListener type>
, simplement parce que le type doit implémenter l' ActionListener
interface qui a exactement ce nom de méthode processAction
défini.
<f:ajax>
, vous préférez en cas de composants de commande utiliser l' action
attribut pour les actions commerciales. Par exemple <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>
.
Comme BalusC l'a indiqué, les actionListener
exceptions par défaut avalent , mais dans JSF 2.0, il y a un peu plus à cela. À savoir, il ne fait pas qu'avaler et enregistrer, mais publie en fait l'exception.
Cela se produit via un appel comme celui-ci:
context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,
new ExceptionQueuedEventContext(context, exception, source, phaseId)
);
L'écouteur par défaut pour cet événement est celui ExceptionHandler
qui, pour Mojarra, est défini surcom.sun.faces.context.ExceptionHandlerImpl
. Cette implémentation renverra essentiellement toute exception, sauf lorsqu'elle concerne une AbortProcessingException, qui est journalisée. ActionListeners encapsule l'exception qui est levée par le code client dans une telle AbortProcessingException qui explique pourquoi ceux-ci sont toujours enregistrés.
Cela ExceptionHandler
peut cependant être remplacé dans faces-config.xml avec une implémentation personnalisée:
<exception-handlerfactory>
com.foo.myExceptionHandler
</exception-handlerfactory>
Au lieu d'écouter globalement, un seul bean peut également écouter ces événements. Ce qui suit en est une preuve de concept:
@ManagedBean
@RequestScoped
public class MyBean {
public void actionMethod(ActionEvent event) {
FacesContext.getCurrentInstance().getApplication().subscribeToEvent(ExceptionQueuedEvent.class, new SystemEventListener() {
@Override
public void processEvent(SystemEvent event) throws AbortProcessingException {
ExceptionQueuedEventContext content = (ExceptionQueuedEventContext)event.getSource();
throw new RuntimeException(content.getException());
}
@Override
public boolean isListenerForSource(Object source) {
return true;
}
});
throw new RuntimeException("test");
}
}
(notez que ce n'est pas ainsi que l'on devrait normalement coder les écouteurs, c'est uniquement à des fins de démonstration!)
Appelant cela à partir d'un Facelet comme ceci:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:body>
<h:form>
<h:commandButton value="test" actionListener="#{myBean.actionMethod}"/>
</h:form>
</h:body>
</html>
Cela entraînera l'affichage d'une page d'erreur.
ActionListener est déclenché en premier, avec une option pour modifier la réponse, avant que Action ne soit appelée et détermine l'emplacement de la page suivante.
Si vous avez plusieurs boutons sur la même page qui doivent aller au même endroit mais faire des choses légèrement différentes, vous pouvez utiliser la même action pour chaque bouton, mais utilisez un ActionListener différent pour gérer des fonctionnalités légèrement différentes.
Voici un lien qui décrit la relation:
TL; DR :
Les ActionListener
s (il peut y en avoir plusieurs) s'exécutent dans l'ordre où ils ont été enregistrés AVANT leaction
Réponse longue :
Une entreprise action
appelle généralement un service EJB et, si nécessaire, définit également le résultat final et / ou navigue vers une vue différente si ce n'est pas ce que vous faites et actionListener
est plus approprié, c'est-à-dire lorsque l'utilisateur interagit avec les composants, tels que h:commandButton
ou h:link
ils peuvent être géré en passant le nom de la méthode du bean géré dans l' actionListener
attribut d'un composant d'interface utilisateur ou pour implémenter une ActionListener
interface et passer le nom de la classe d'implémentation à l' actionListener
attribut d'un composant d'interface utilisateur.