web-dev-qa-db-fra.com

Différences entre action et actionListener

Quelle est la différence entre action et actionListener et quand devrais-je utiliser action par rapport à actionListener?

366
Murat Güzel

actionListener

Utilisez actionListenersi 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 ActionEventNAME _ argument). Donc, uniquement à des fins de préparation avant que la véritable action commerciale soit invoquée.

La méthode actionListenera 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 ActionEventen 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 ActionEventname__.

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 bindingname__. 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 bindingest 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?


action

Utilisez actionsi vous souhaitez exécuter une action commerciale et si nécessaire gérer la navigation. La méthode actionpeut (donc ne doit pas obligatoirement) renvoyer un Stringqui sera utilisé comme résultat de cas de navigation (la vue cible). Une valeur de retour de nullou voidlui 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 actionpeut être toute méthode MethodExpressionNAME _ 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 actionname__. 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) .


f: auditeur ajax

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 ajaxListenera par défaut la signature suivante:

_import javax.faces.event.AjaxBehaviorEvent;
// ...

public void ajaxListener(AjaxBehaviorEvent event) {
    // ...
}
_

Dans Mojarra, l'argument AjaxBehaviorEventest facultatif, ci-dessous fonctionne comme il convient.

_public void ajaxListener() {
    // ...
}
_

Mais dans MyFaces, il y aurait un MethodNotFoundExceptionname__. 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 à actionet/ou actionListenerpour plus de clarté et un meilleur code auto-documenté. De plus, comme actionListenername__, 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 executeet rendername__, allez à Comprendre le processus/la mise à jour de PrimeFaces et les attributs JSF f: ajax execute/render .


Ordre d'invocation

Les actionListenername__s sont toujours appelés avant le actiondans 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:

  1. Bean#ajaxListener()
  2. Bean#actionListener()
  3. ActionListenerType#processAction()
  4. Bean#actionListenerBinding()
  5. Bean#setProperty()
  6. Bean#action()

Gestion des exceptions

actionListenerprend en charge une exception spéciale: AbortProcessingExceptionNAME _ . Si cette exception est levée à partir d'une méthode actionListenername__, 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 actionListenername__. 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 actionname__.

Si l'unique raison d'utiliser actionListenerest de renvoyer une méthode voidà la même page, c'est une mauvaise méthode. Les méthodes actionpeuvent également parfaitement renvoyer voidname__, 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 actionListenername__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 listenerde _<f:ajax>_ ou non. Pour des explications et un exemple, allez à Traitement des exceptions dans les demandes JSF ajax .

562
BalusC

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.

45
Arjan Tijms

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:

http://www.Java-samples.com/showtutorial.php?tutorialid=605

39
Erick Robertson

TL; DR :

Les ActionListeners (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.

0
Yehuda Schwartz