404 votes

Différences entre action et actionListener

Quelle est la différence entre action et actionListener et quand dois-je utiliser action contre actionListener ?

605voto

BalusC Points 498232

ActionListener

Utilisez actionListener si vous voulez avoir un crochet avant l'action commerciale réelle a été exécutée, par exemple pour l'enregistrer, et/ou pour définir une propriété supplémentaire (en <f:setPropertyActionListener> ), et/ou pour avoir accès au composant qui a invoqué l'action (qui est disponible par ActionEvent argument). Donc, purement à des fins de préparation avant que la véritable action commerciale ne soit invoquée.

Le site actionListener 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 parenthèses de méthode :

<h:commandXxx ... actionListener="#{bean.actionListener}" />

Notez que vous ne pouvez pas passer supplémentaire par EL 2.2. Vous pouvez toutefois remplacer l'option ActionEvent en passant et en spécifiant un ou plusieurs arguments personnalisés. Les exemples suivants sont valables :

<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 à ce qu'une méthode avec le paramètre ActionEvent argument.

Si vous êtes sous EL 2.2+, vous pouvez 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'expression binding attribut. S'il n'y en avait pas, EL enverrait un message d'erreur de type javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean parce que le 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 également 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 action si vous souhaitez exécuter une action commerciale et, le cas échéant, gérer la navigation. Le site action peut (donc, pas obligatoirement) retourner une méthode String qui sera utilisé comme résultat du 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 l'étendue de la vue actuelle. Une valeur de retour d'une chaîne vide ou du même identifiant de vue permettra également de retourner à la même page, mais recréera l'étendue de la vue et détruira ainsi tous les beans actuellement actifs et, le cas échéant, les recréera.

Le site action peut être n'importe quelle méthode valide MethodExpression ainsi que ceux 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 ne renvoie qu'une chaîne de caractères, vous pouvez également spécifier exactement cette chaîne de caractères dans la balise action attribut. C'est donc totalement maladroit :

<h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />

Avec cette méthode insensée qui renvoie 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 cela indique à son tour une mauvaise conception : la navigation par POST. Cette méthode n'est pas conviviale pour l'utilisateur ni pour le référencement. Tout ceci 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 en JSF ? Comment faire en sorte que l'URL reflète la page actuelle (et non la précédente) ? .


f:ajax listener

Depuis JSF 2.x, il y a une troisième voie, le <f:ajax listener> .

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

Le site ajaxListener a par défaut la signature suivante :

import javax.faces.event.AjaxBehaviorEvent;
// ...

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

À Mojarra, le AjaxBehaviorEvent est facultatif, le paramètre ci-dessous fonctionne aussi bien.

public void ajaxListener() {
    // ...
}

Mais dans MyFaces, cela déclencherait un MethodNotFoundException . Below fonctionne dans les deux implémentations de 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 sur les composants de saisie et de sélection <h:inputXxx> / <h:selectXxx> . Dans les composants de commande, il suffit de s'en tenir à action et/ou actionListener pour plus de clarté et un meilleur code auto-documenté. De plus, comme actionListener le f:ajax listener ne permet pas de renvoyer un résultat de navigation.

<h:commandXxx ... action="#{bean.action}">
    <f:ajax execute="@form" render="@form" />
</h:commandXxx>

Pour une explication sur execute et render attributs, rendez-vous sur Comprendre les attributs PrimeFaces process/update et JSF f:ajax execute/render .


Ordre d'invocation

Le site actionListener sont toujours invoqués avant le site action dans l'ordre où ils ont été déclarés dans la vue et attachés au composant. Le site f:ajax listener est toujours invoqué avant n'importe quel é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>

Les méthodes seront invoquées dans l'ordre suivant :

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

Traitement des exceptions

Le site actionListener soutient une exception spéciale : AbortProcessingException . Si cette exception est levée à partir d'un actionListener JSF ignorera tous les autres écouteurs d'action et la méthode d'action et rendra directement la réponse. Vous ne verrez pas de page d'erreur/exception, mais JSF l'enregistrera. Cette opération sera également effectuée implicitement chaque fois qu'une autre exception sera levée par une méthode d'action. actionListener . Ainsi, si vous avez l'intention de bloquer la page par une page d'erreur à la suite d'une exception commerciale, vous devez absolument effectuer le travail dans la section action méthode.

Si la seule raison d'utiliser un actionListener est d'avoir un void qui revient à la même page, alors c'est une mauvaise méthode. Le site action peuvent parfaitement retourner aussi void contrairement à ce que certains IDEs vous laissent croire via la validation EL. Notez que le Vitrine PrimeFaces Les exemples sont truffés de ce genre de actionListener sur toute la place. C'est vraiment mauvais. N'utilisez pas ça comme une excuse pour le faire vous aussi.

Dans les requêtes ajax, cependant, un gestionnaire d'exception spécial est nécessaire. Ceci indépendamment du fait que vous utilisiez listener l'attribut de <f:ajax> ou pas. Pour une explication et un exemple, rendez-vous sur Traitement des exceptions dans les requêtes JSF ajax .

1 votes

Vous avez raison de dire que les exceptions dans les actionListeners sont avalées par défaut, mais dans JSF 2.0 ce comportement peut être modifié. Voir ma réponse ci-dessous pour plus de détails.

3 votes

@arjan : vous avez raison de dire que JSF 2.0 vous permet de modifier le traitement par défaut des exceptions lancées par actionListener mais cela n'en fait pas pour autant une bonne excuse pour abuser de l'argent. actionListener pour entreprise actions.

1 votes

En effet, les actions de l'entreprise se trouvent dans le "flux" principal du cycle demande/réponse et seuls les action correspond à cela. actionListener est pour les affaires secondaires. Je voulais juste préciser que les exceptions de actionListener peuvent être propagées si nécessaire ;)

48voto

Arjan Tijms Points 21682

Comme l'a indiqué BalusC, le actionListener par défaut avale les exceptions, mais dans JSF 2.0 il y a un peu plus que cela. En effet, il ne se contente pas d'avaler et d'enregistrer, mais en fait publie l'exception.

Cela se fait 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 le ExceptionHandler qui, pour Mojarra, est fixé à com.sun.faces.context.ExceptionHandlerImpl . Cette mise en œuvre relance toutes les exceptions, sauf lorsqu'il s'agit d'une AbortProcessingException, qui est enregistrée. Les ActionListeners enveloppent l'exception levée par le code client dans une telle AbortProcessingException, ce qui explique pourquoi elles sont toujours enregistrées.

Ce site ExceptionHandler peut cependant être remplacé dans faces-config.xml par 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 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");
    }

}

(attention, ce n'est pas la façon dont on devrait normalement coder les écouteurs, ceci n'est qu'à des fins de démonstration !)

J'appelle ça d'un Facelet comme celui-ci :

<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>

Une page d'erreur s'affichera.

45voto

Erick Robertson Points 12958

L'ActionListener est déclenché en premier, avec la possibilité de modifier la réponse, avant que l'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 une fonctionnalité légèrement différente.

Voici un lien qui décrit cette relation :

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

3 votes

Plus un, Les lettres en gras disent presque tout.

0voto

Yehuda Schwartz Points 1211

TL;DR :

Le site ActionListener (il peut y en avoir plusieurs) s'exécutent dans l'ordre dans lequel ils ont été enregistrés AVANT l'exécution de l action

Réponse longue :

Une entreprise action invoque généralement un service EJB et, si nécessaire, définit également le résultat final et/ou navigue vers une autre vue. si ce n'est pas ce que vous faites, une actionListener est plus approprié, c'est-à-dire pour le moment où l'utilisateur interagit avec les composants, par exemple h:commandButton ou h:link ils peuvent être gérés en passant le nom de la méthode du bean géré dans le champ actionListener d'un composant d'interface utilisateur ou pour mettre en œuvre un attribut ActionListener et transmet le nom de la classe d'implémentation à actionListener d'un composant d'interface utilisateur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X