Qual è la differenza tra action
e actionListener
e quando dovrei usare action
versus actionListener
?
Qual è la differenza tra action
e actionListener
e quando dovrei usare action
versus actionListener
?
Risposte:
Utilizzare actionListener
se 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 ActionEvent
discussione). Quindi, puramente per preparare gli scopi prima che venga invocata la vera azione commerciale.
Il actionListener
metodo 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 ActionEvent
tutto 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 ActionEvent
argomento.
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 binding
nell'attributo. Se fossero assenti, EL lancerebbe confusamente a javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean
, poiché l' binding
attributo 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?
Utilizzare action
se si desidera eseguire un'azione commerciale e, se necessario, gestire la navigazione. Il action
metodo può (quindi, non è necessario) restituire un String
che verrà utilizzato come risultato del caso di navigazione (la vista target). Un valore di ritorno di null
o lo void
farà 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 action
metodo 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 action
nell'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) .
Dal momento che JSF 2.x c'è un terzo modo, il <f:ajax listener>
.
<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
Il ajaxListener
metodo ha di default la seguente firma:
import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
In Mojarra, l' AjaxBehaviorEvent
argomento è 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 action
e / o actionListener
per chiarezza e un migliore codice di auto-documentazione. Inoltre, come actionListener
, il f:ajax listener
non supporta la restituzione di un risultato di navigazione.
<h:commandXxx ... action="#{bean.action}">
<f:ajax execute="@form" render="@form" />
</h:commandXxx>
Per spiegazioni execute
e render
attributi, vai a Comprensione processo / aggiornamento PrimeFaces e JSF f: ajax esegui / rende gli attributi .
I actionListener
s sono sempre invocati prima della action
nello 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:
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
Il actionListener
supporta un'eccezione speciale: AbortProcessingException
. Se questa eccezione viene generata da un actionListener
metodo, 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 action
metodo.
Se l'unico motivo per utilizzare un actionListener
è avere un void
metodo per tornare alla stessa pagina, allora è un cattivo. I action
metodi 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 actionListener
s 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' listener
attributo <f:ajax>
. Per una spiegazione e un esempio, vai alla gestione delle eccezioni nelle richieste ajax di JSF .
actionListener
, ma ciò non lo rende ancora una buona scusa per abusare actionListener
di azioni aziendali .
action
corrispondenti. actionListener
è per cose secondarie. Volevo solo chiarire che le eccezioni da actionListener
s possono essere propagate se così richiesto;)
actionListener
nell'attributo e deve esserlo public
anche. Il processAction
nome è obbligatorio solo quando si utilizza <f:actionListener type>
, semplicemente perché il tipo deve implementare l' ActionListener
interfaccia che ha esattamente quel nome di metodo processAction
definito.
<f:ajax>
, in caso di componenti di comando si preferisce utilizzare l' action
attributo per le azioni aziendali. Es <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>
.
Come indicato da BalusC, l' actionListener
impostazione 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 ExceptionHandler
cui è 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 ExceptionHandler
può 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.
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:
TL; DR :
Gli ActionListener
s (possono essercene più) vengono eseguiti nell'ordine in cui sono stati registrati PRIMA delaction
Risposta lunga :
Un'azienda in action
genere 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:commandButton
o h:link
possono essere gestito passando il nome del metodo bean gestito actionListener
nell'attributo di un componente dell'interfaccia utente o per implementare ActionListener
un'interfaccia e passare il nome della classe di implementazione actionListener
all'attributo di un componente dell'interfaccia utente.