Différences entre action et actionListener

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

360
demandé sur BalusC 2010-10-11 23:20:31

3 réponses

actionListener

Utiliser actionListener si vous voulez avoir un crochet avant l'activité réelle de l'action est exécutée, par exemple, pour se connecter, et/ou de définir une propriété supplémentaire (par <f:setPropertyActionListener> ), et/ou d'avoir accès à la composante qui a invoqué l'action (qui est disponible en ActionEvent argument). Donc, purement à des fins de préparation avant que l'action réelle des affaires ne 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 il est censé être déclaré comme suit, sans aucune méthode entre parenthèses:

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

notez que vous ne pouvez pas passer arguments supplémentaires par EL 2.2. Vous pouvez cependant outrepasser complètement l'argument ActionEvent en passant et en spécifiant l'argument 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 méthode sans argumentation. S'ils étaient absents, JSF s'attendrait toujours à une méthode avec l'argument ActionEvent .

si vous êtes sur EL 2.2+, alors vous pouvez déclarer des méthodes d'écoute à action multiple 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 . Si elles ont été absent, EL lancerait de façon confuse un javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean , parce que l'attribut binding est par défaut interprété 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 façon transparente une expression de valeur en une expression de méthode. Voir aussi a. O. Pourquoi suis-je capable de lier à une méthode arbitraire si elle n'est pas supportée par JSF?


action

Use action si vous souhaitez exécuter une action d'affaires et si nécessaire gérer la navigation. La méthode action peut (donc pas doit) retourner 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 retourner à la même page et de garder le champ de vue actuel vivant. Une valeur de retour d'une chaîne vide ou le même ID de vue retournera aussi à la même page, mais recréera le champ de vue et détruira ainsi n'importe quel actuellement active view scoped beans et, s'il y a lieu, recreate them.

la méthode action peut être n'importe quelle méthode valide MethodExpression , aussi 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 renvoie uniquement une chaîne de caractères, vous pouvez également spécifier exactement cette chaîne de caractères dans l'attribut action . Ainsi, c'est tout à fait maladroit:

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

avec cette méthode insensée retournant une chaîne codée en dur:

public String goToNextpage() {
    return "nextpage";
}

au lieu de cela, il suffit de mettre cette chaîne de caractères hardcoded directement dans l'attribut:

<h:commandLink value="Go to next page" action="nextpage" />

s'il vous plaît noter que cela à son tour indique une mauvaise conception: la navigation par la poste. Ce n'est pas l'utilisateur, ni SEO friendly. Tout cela est expliqué dans quand devrais-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 pour 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, la <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 bon.

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

mais dans mon visage, il lancerait un MethodNotFoundException . Ci-dessous fonctionne dans les deux implémentations JSF lorsque vous voulez 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 sélectionnez les composants <h:inputXxx> / <h:selectXxx> . Dans les composants de commande, il suffit de s'en tenir à action et/ou actionListener pour plus de clarté et une meilleure auto-documentation du code. En outre, comme actionListener , le f:ajax listener ne supporte pas le retour d'un résultat de navigation.

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

Pour l'explication sur la execute et render attributs, de la tête aux la Compréhension de PrimeFaces/processus de mise à jour et JSF f:ajax exécuter/render attributs .


l'Invocation de l'ordre

les actionListener s sont toujours invoqués avant le action dans le même ordre qu'ils sont déclarés dans la vue et attachés à l'élément. Le f:ajax listener est toujours invoqué avant tout auditeur 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>

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

traitement des exceptions

le actionListener supporte une exception spéciale: AbortProcessingException . Si cette exception est rejetée d'une méthode actionListener , alors JSF sautera toute reste des écouteurs d'action et de la méthode d'action et procéder au rendu de la réponse directement. Vous ne verrez pas de page d'erreur/d'exception, JSF l'enregistrera cependant. Cela sera également fait implicitement chaque fois qu'une autre exception est jetée d'un actionListener . Ainsi, si vous avez l'intention de bloquer la page par une page d'erreur à la suite d'une exception d'entreprise, alors vous devriez certainement effectuer le travail dans la méthode action .

Si la seule raison d'utiliser un actionListener est d'avoir un void méthode retournant à la même page, alors que c'est une mauvaise. Les méthodes action peuvent parfaitement aussi retourner void , au contraire de ce que certains IDEs laissent croire via la validation EL. Notez que les exemples PrimeFaces showcase sont parsemés de ce genre de actionListener partout. C'est en effet mal. Ne vous en servez pas comme excuse pour le faire vous-même.

Dans les requêtes ajax, cependant, un exception spéciale handler est nécessaire. Cela est indépendamment du fait que vous utilisez listener attribut de <f:ajax> ou non. Pour une explication et un exemple, allez à traitement des exceptions dans les requêtes ajax JSF .

535
répondu BalusC 2017-05-23 11:47:26

comme L'a indiqué BalusC, le actionListener par défaut avale des exceptions, mais dans JSF 2.0 Il y a un peu plus à cela. À savoir, il ne se contente pas d'avaler et les journaux, mais en fait publie l'exception.

cela se produit à travers un appel comme celui-ci:

context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,                                                          
    new ExceptionQueuedEventContext(context, exception, source, phaseId)
);

l'auditeur par défaut pour cet événement est le ExceptionHandler qui pour Mojarra est défini à com.sun.faces.context.ExceptionHandlerImpl . Cette mise en œuvre permettra essentiellement de exception, sauf lorsqu'il s'agit d'un AbortProcessingException, qui est connecté. ActionListeners enveloppent l'exception qui est lancée par le code client dans un tel AbortProcessingException qui explique pourquoi ceux-ci sont toujours journalisés.

ce ExceptionHandler peut toutefois ê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 haricot peut aussi é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 ça qu'il faut normalement coder les écouteurs, c'est seulement à des fins de démonstration!)

L'appelant 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>

affichera une page d'erreur.

44
répondu Arjan Tijms 2010-12-19 14:40:50

ActionListener est tiré en premier, avec une option 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 devraient 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

37
répondu Erick Robertson 2010-10-11 19:38:54