Quelle est la différence entre action
et actionListener
et quand devrais-je utiliser action
par rapport à actionListener
?
Utilisez actionListener
si vous souhaitez avoir un point d'ancrage avant l'action réelle doit être exécutée, par exemple. pour le consigner et/ou pour définir une propriété supplémentaire (avec <f:setPropertyActionListener>
), et/ou pour avoir accès au composant qui a appelé l'action (qui est disponible pour ActionEvent
NAME _ argument). Donc, uniquement à des fins de préparation avant que la véritable action commerciale soit invoquée.
La méthode actionListener
a par défaut la signature suivante:
_import javax.faces.event.ActionEvent;
// ...
public void actionListener(ActionEvent event) {
// ...
}
_
Et cela est supposé être déclaré comme suit, sans aucune méthode entre parenthèses:
_<h:commandXxx ... actionListener="#{bean.actionListener}" />
_
Notez que vous ne pouvez pas transmettre des arguments supplémentaires par EL 2.2. Vous pouvez cependant remplacer l'argument ActionEvent
en passant et en spécifiant un ou plusieurs 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 méthode sans argument. S'ils étaient absents, JSF attendrait toujours une méthode avec l'argument ActionEvent
name__.
Si vous êtes sur EL 2.2+, vous pouvez alors déclarer plusieurs méthodes d’écoute d’action 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’attribut binding
name__. S'ils étaient absents, EL émettrait de manière confuse un _javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean
_, car l'attribut binding
est interprété par défaut comme une expression de valeur et non 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 expression de méthode. Voir aussi a.o. Pourquoi suis-je capable de lier <f: actionListener> à une méthode arbitraire si elle n'est pas supportée par JSF?
Utilisez action
si vous souhaitez exécuter une action commerciale et si nécessaire gérer la navigation. La méthode action
peut (donc ne doit pas obligatoirement) renvoyer un String
qui sera utilisé comme résultat de cas de navigation (la vue cible). Une valeur de retour de null
ou void
lui permettra de revenir à la même page et de conserver la portée de la vue actuelle. Une valeur de retour d'une chaîne vide ou le même ID de vue renverra également à la même page, mais recréera la portée de la vue et détruira ainsi tous les beans périmés de la vue actuellement active et, le cas échéant, les recréera.
La méthode action
peut être toute méthode MethodExpression
NAME _ valide, ainsi que celles qui utilisent des arguments EL 2.2 tels que 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'attribut action
name__. Donc, c'est 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, il suffit de mettre cette chaîne codée en dur directement dans l'attribut:
_<h:commandLink value="Go to next page" action="nextpage" />
_
Veuillez noter que ceci indique à son tour une mauvaise conception: navigation par POST. Ce n'est pas utilisateur ni SEO friendly. Tout cela est expliqué dans Quand devrais-je utiliser h: outputLink au lieu de h: commandLink? et est supposé ê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 un troisième moyen, le _<f:ajax listener>
_.
_<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
_
La méthode ajaxListener
a par défaut la signature suivante:
_import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
_
Dans Mojarra, l'argument AjaxBehaviorEvent
est facultatif, ci-dessous fonctionne comme il convient.
_public void ajaxListener() {
// ...
}
_
Mais dans MyFaces, il y aurait un MethodNotFoundException
name__. 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 lors de la saisie et de la sélection des composants _<h:inputXxx>
_/_<h:selectXxx>
_. Dans les composants de commande, tenez-vous-en à action
et/ou actionListener
pour plus de clarté et un meilleur code auto-documenté. De plus, comme actionListener
name__, 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 plus d'explications sur les attributs execute
et render
name__, allez à Comprendre le processus/la mise à jour de PrimeFaces et les attributs JSF f: ajax execute/render .
Les actionListener
name__s sont toujours appelés avant le action
dans le même ordre où ils ont été déclarés dans la vue et attachés au composant. Le _f:ajax listener
_ est toujours appelé avant tout écouteur d'action. Donc, 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>
_
Invoquera les méthodes dans l'ordre suivant:
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
actionListener
prend en charge une exception spéciale: AbortProcessingException
NAME _ . Si cette exception est levée à partir d'une méthode actionListener
name__, JSF ignorera alors les écouteurs d'action restants et la méthode d'action, puis affichera directement la réponse. Vous ne verrez pas de page d'erreur/exception, JSF le consignera cependant. Cela sera également implicitement effectué chaque fois qu'une autre exception est levée à partir d'un actionListener
name__. Ainsi, si vous avez l'intention de bloquer la page par une page d'erreur à la suite d'une exception métier, vous devez absolument effectuer le travail à l'aide de la méthode action
name__.
Si l'unique raison d'utiliser actionListener
est de renvoyer une méthode void
à la même page, c'est une mauvaise méthode. Les méthodes action
peuvent également parfaitement renvoyer void
name__, contrairement à ce que certains IDE vous laissent croire via la validation EL. Notez que les exemples PrimeFaces showcase sont parsemés de ce genre de actionListener
name__s partout. C'est vraiment faux. N'utilisez pas ceci comme excuse pour le faire vous-même.
Dans les demandes ajax, cependant, un gestionnaire d'exception spécial est nécessaire. Que vous utilisiez l'attribut listener
de _<f:ajax>
_ ou non. Pour des explications et un exemple, allez à Traitement des exceptions dans les demandes JSF ajax .
Comme BalusC l'a indiqué, la variable actionListener
par défaut engloutit des exceptions, mais dans JSF 2.0, il y a un peu plus que cela. À savoir, il ne se contente pas d’avaler et de consigner des journaux, mais en réalité publishes l’exception.
Cela se produit par 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 la ExceptionHandler
qui, pour Mojarra, est définie sur com.Sun.faces.context.ExceptionHandlerImpl
. Cette implémentation renverra fondamentalement toute exception, sauf s’il s’agit d’une exception AbortProcessingException, qui est consignée. Les ActionListeners encapsulent l'exception qui est levée par le code client dans une telle exception AbortProcessingException, ce qui explique pourquoi ces derniers sont toujours journalisés.
Cette ExceptionHandler
peut cependant être remplacée dans faces-config.xml par une implémentation personnalisée:
<exception-handlerfactory>
com.foo.myExceptionHandler
</exception-handlerfactory>
Au lieu d'écouter globalement, un seul haricot peut également écouter ces événements. Ce qui suit est une preuve de concept de ceci:
@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 comme cela qu'on devrait normalement coder les auditeurs, c'est uniquement à des fins de démonstration!)
Appelant cela depuis 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 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 utiliser 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 dans lequel ils ont été enregistrés AVANT le action
Longue réponse :
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, une actionListener
est plus appropriée, par exemple lorsque l'utilisateur interagit avec les composants, tels que h:commandButton
ou h:link
, ils peuvent être gérés en transmettant le nom de la méthode de bean géré dans l'attribut actionListener
d'un composant d'interface utilisateur ou en implémentant une interface ActionListener
et en transmettant le nom de la classe d'implémentation à l'attribut actionListener
d'un composant UI.