Differenze tra action e actionListener


Risposte:


583

ActionListener

Utilizzare actionListenerse si desidera avere un hook prima che venga eseguita l'azione aziendale reale, ad es. Per registrarlo e / o per impostare una proprietà aggiuntiva (by <f:setPropertyActionListener>) e / o per avere accesso al componente che ha invocato l'azione (che è disponibile da ActionEventdiscussione). Quindi, puramente per preparare gli scopi prima che venga invocata la vera azione commerciale.

Il actionListenermetodo ha di default la seguente firma:

import javax.faces.event.ActionEvent;
// ...

public void actionListener(ActionEvent event) {
    // ...
}

E dovrebbe essere dichiarato come segue, senza parentesi di metodo:

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

Si noti che non è possibile passare argomenti aggiuntivi da EL 2.2. È comunque possibile ignorare del ActionEventtutto l' argomento passando e specificando argomenti personalizzati. Sono validi i seguenti esempi:

<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) {}

Nota l'importanza delle parentesi nell'espressione del metodo senza argomenti. Se fossero assenti, JSF si aspetterebbe comunque un metodo con ActionEventargomento.

Se utilizzi EL 2.2+, puoi dichiarare più metodi listener di azioni tramite <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() {}

Nota l'importanza delle parentesi bindingnell'attributo. Se fossero assenti, EL lancerebbe confusamente a javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean, poiché l' bindingattributo viene interpretato di default come espressione di valore, non come espressione di metodo. L'aggiunta di parentesi di stile EL 2.2+ trasforma in modo trasparente un'espressione di valore in un'espressione di metodo. Vedi anche ao Perché sono in grado di associare <f: actionListener> a un metodo arbitrario se non è supportato da JSF?


azione

Utilizzare actionse si desidera eseguire un'azione commerciale e, se necessario, gestire la navigazione. Il actionmetodo può (quindi, non è necessario) restituire un Stringche verrà utilizzato come risultato del caso di navigazione (la vista target). Un valore di ritorno di nullo lo voidfarà tornare alla stessa pagina e manterrà vivo l'ambito della vista corrente. Un valore di ritorno di una stringa vuota o dello stesso ID vista tornerà anche alla stessa pagina, ma ricrea l'ambito della vista e quindi distrugge tutti i bean con ambito vista attualmente attivi e, se applicabile, li ricrea.

Il actionmetodo può essere qualsiasi valido MethodExpression, anche quelli che utilizzano argomenti EL 2.2 come di seguito:

<h:commandXxx value="submit" action="#{bean.edit(item)}" />

Con questo metodo:

public void edit(Item item) {
    // ...
}

Nota che quando il tuo metodo di azione restituisce solo una stringa, puoi anche specificare esattamente quella stringa actionnell'attributo. Quindi, questo è totalmente goffo:

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

Con questo metodo insensato che restituisce una stringa hardcoded:

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

Invece, basta inserire quella stringa codificata direttamente nell'attributo:

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

Si noti che questo a sua volta indica un design errato: navigare tramite POST. Questo non è user né SEO friendly. Tutto questo è spiegato in Quando dovrei usare h: outputLink invece di h: commandLink? e dovrebbe essere risolto come

<h:link value="Go to next page" outcome="nextpage" />

Vedi anche Come navigare in JSF? Come fare in modo che l'URL rifletta la pagina corrente (e non quella precedente) .


f: ascoltatore ajax

Dal momento che JSF 2.x c'è un terzo modo, il <f:ajax listener>.

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

Il ajaxListenermetodo ha di default la seguente firma:

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

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

In Mojarra, l' AjaxBehaviorEventargomento è facoltativo, di seguito funziona bene.

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

Ma in MyFaces, verrebbe lanciato a MethodNotFoundException. Di seguito funziona in entrambe le implementazioni JSF quando si desidera omettere l'argomento.

<h:commandXxx ...>
    <f:ajax execute="@form" listener="#{bean.ajaxListener()}" render="@form" />
</h:commandXxx>

I listener Ajax non sono molto utili sui componenti di comando. Sono più utili sull'input e selezionano i componenti <h:inputXxx>/ <h:selectXxx>. Nei componenti di comando, attenersi a actione / o actionListenerper chiarezza e un migliore codice di auto-documentazione. Inoltre, come actionListener, il f:ajax listenernon supporta la restituzione di un risultato di navigazione.

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

Per spiegazioni executee renderattributi, vai a Comprensione processo / aggiornamento PrimeFaces e JSF f: ajax esegui / rende gli attributi .


Ordine di invito

I actionListeners sono sempre invocati prima della actionnello stesso ordine in cui sono stati dichiarati nella vista e fissati al componente. Il f:ajax listenerè sempre invocato prima di qualsiasi ascoltatore azione. Quindi, il seguente esempio:

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

Richiamerà i metodi nel seguente ordine:

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

La gestione delle eccezioni

Il actionListenersupporta un'eccezione speciale: AbortProcessingException. Se questa eccezione viene generata da un actionListenermetodo, JSF salterà tutti i listener di azioni rimanenti e il metodo di azione e procederà al rendering diretto della risposta. Non verrà visualizzata una pagina di errore / eccezione, tuttavia JSF la registrerà. Questo sarà anche implicitamente fatto ogni volta che viene generata un'altra eccezione da un actionListener. Pertanto, se si intende bloccare la pagina con una pagina di errore a seguito di un'eccezione aziendale, è necessario eseguire il lavoro con questo actionmetodo.

Se l'unico motivo per utilizzare un actionListenerè avere un voidmetodo per tornare alla stessa pagina, allora è un cattivo. I actionmetodi possono anche restituire perfettamente void, al contrario di ciò che alcuni IDE ti consentono di credere tramite la convalida EL. Si noti che gli esempi di vetrine PrimeFaces sono disseminati di questo tipo di actionListeners su tutto il luogo. Questo è davvero sbagliato. Non usarlo come scusa per farlo anche tu.

Nelle richieste Ajax, tuttavia, è necessario un gestore di eccezioni speciale. Questo indipendentemente dal fatto che tu usi o meno l' listenerattributo <f:ajax>. Per una spiegazione e un esempio, vai alla gestione delle eccezioni nelle richieste ajax di JSF .


1
Hai ragione nel dire che le eccezioni in actionListeners vengono ingerite per impostazione predefinita, ma in JSF 2.0 questo comportamento può essere modificato. Vedi la mia risposta qui sotto per i dettagli.
Arjan Tijms,

3
@arjan: hai ragione sul fatto che JSF 2.0 ti consente di modificare la gestione predefinita delle eccezioni generate actionListener, ma ciò non lo rende ancora una buona scusa per abusare actionListenerdi azioni aziendali .
BalusC

1
In effetti, le azioni imprenditoriali sono nel "flusso" principale del ciclo di richiesta / risposta e solo le actioncorrispondenti. actionListenerè per cose secondarie. Volevo solo chiarire che le eccezioni da actionListeners possono essere propagate se così richiesto;)
Arjan Tijms

2
@Kawy: il nome del metodo è libero quando viene utilizzato actionListenernell'attributo e deve esserlo publicanche. Il processActionnome è obbligatorio solo quando si utilizza <f:actionListener type>, semplicemente perché il tipo deve implementare l' ActionListenerinterfaccia che ha esattamente quel nome di metodo processActiondefinito.
BalusC,

2
@Muhammed: il listener di azioni Ajax viene invocato prima di tutti i listener di azioni regolari. Si noti che anche quando si utilizza <f:ajax>, in caso di componenti di comando si preferisce utilizzare l' actionattributo per le azioni aziendali. Es <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>.
BalusC,

47

Come indicato da BalusC, l' actionListenerimpostazione predefinita ingoia eccezioni, ma in JSF 2.0 c'è un po 'di più in questo. Vale a dire, non solo ingoia e registra, ma in realtà pubblica l'eccezione.

Questo accade attraverso una chiamata come questa:

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

Il listener predefinito per questo evento è quello su ExceptionHandlercui è impostato Mojarra com.sun.faces.context.ExceptionHandlerImpl. Questa implementazione riproporrà sostanzialmente qualsiasi eccezione, tranne quando si tratta di AbortProcessingException, che viene registrato. Gli ActionListener racchiudono l'eccezione generata dal codice client in tale AbortProcessingException che spiega perché vengono sempre registrati.

Questo ExceptionHandlerpuò essere sostituito tuttavia in faces-config.xml con un'implementazione personalizzata:

<exception-handlerfactory>
   com.foo.myExceptionHandler
</exception-handlerfactory>

Invece di ascoltare a livello globale, un singolo bean può anche ascoltare questi eventi. Quanto segue è una prova del concetto di questo:

@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");
    }

}

(nota, questo non è come si dovrebbe normalmente codificare gli ascoltatori, questo è solo a scopo dimostrativo!)

Chiamando questo da una Facelet in questo modo:

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

Verrà visualizzata una pagina di errore.


43

ActionListener viene attivato per primo, con un'opzione per modificare la risposta, prima che venga chiamato Action e determini la posizione della pagina successiva.

Se hai più pulsanti nella stessa pagina che dovrebbero andare nello stesso posto ma fare cose leggermente diverse, puoi usare la stessa azione per ogni pulsante, ma utilizzare un ActionListener diverso per gestire funzionalità leggermente diverse.

Ecco un link che descrive la relazione:

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


3
più uno, le lettere in grassetto dicono quasi tutto.
Shirgill Farhan,

0

TL; DR :

Gli ActionListeners (possono essercene più) vengono eseguiti nell'ordine in cui sono stati registrati PRIMA delaction

Risposta lunga :

Un'azienda in actiongenere richiama un servizio EJB e, se necessario, imposta anche il risultato finale e / o passa a una visione diversa se non è ciò che si sta facendo e actionListenerè più appropriato, ad esempio quando l'utente interagisce con i componenti, come h:commandButtono h:linkpossono essere gestito passando il nome del metodo bean gestito actionListenernell'attributo di un componente dell'interfaccia utente o per implementare ActionListenerun'interfaccia e passare il nome della classe di implementazione actionListenerall'attributo di un componente dell'interfaccia utente.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.