¿Cuál es la diferencia entre action
y actionListener
, y cuándo debo usar action
versus actionListener
?
¿Cuál es la diferencia entre action
y actionListener
, y cuándo debo usar action
versus actionListener
?
Respuestas:
Úselo actionListener
si desea tener un enlace antes de que se ejecute la acción comercial real, por ejemplo, para iniciar sesión y / o para establecer una propiedad adicional (por <f:setPropertyActionListener>
), y / o tener acceso al componente que invocó la acción (que está disponible por ActionEvent
argumento). Por lo tanto, solo con fines de preparación antes de que se invoque la acción comercial real.
El actionListener
método tiene por defecto la siguiente firma:
import javax.faces.event.ActionEvent;
// ...
public void actionListener(ActionEvent event) {
// ...
}
Y se supone que debe declararse de la siguiente manera, sin ningún paréntesis de método:
<h:commandXxx ... actionListener="#{bean.actionListener}" />
Tenga en cuenta que no puede pasar argumentos adicionales por EL 2.2. Sin embargo, puede anular el ActionEvent
argumento por completo pasando y especificando argumentos personalizados. Los siguientes ejemplos son válidos:
<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) {}
Tenga en cuenta la importancia de los paréntesis en la expresión del método sin argumentos. Si estuvieran ausentes, JSF aún esperaría un método con ActionEvent
argumento.
Si está en EL 2.2+, puede declarar varios métodos de escucha de acción a través de <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() {}
Tenga en cuenta la importancia de los paréntesis en el binding
atributo. Si estuvieran ausentes, EL arrojaría confusamente a javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean
, porque el binding
atributo se interpreta por defecto como una expresión de valor, no como una expresión de método. Agregar paréntesis de estilo EL 2.2+ convierte transparentemente una expresión de valor en una expresión de método. Consulte también ao ¿Por qué puedo vincular <f: actionListener> a un método arbitrario si JSF no lo admite?
Úselo action
si desea ejecutar una acción comercial y, si es necesario, manejar la navegación. El action
método puede (por lo tanto, no debe) devolver un String
que se utilizará como resultado del caso de navegación (la vista de destino). Un valor de retorno de null
o void
le permitirá volver a la misma página y mantener vivo el alcance de la vista actual. Un valor de retorno de una cadena vacía o la misma ID de vista también volverá a la misma página, pero recreará el alcance de la vista y, por lo tanto, destruirá cualquier bean de ámbito de vista actualmente activo y, si corresponde, vuelva a crearlos.
El action
método puede ser válido MethodExpression
, también los que usan argumentos EL 2.2 como los siguientes:
<h:commandXxx value="submit" action="#{bean.edit(item)}" />
Con este método:
public void edit(Item item) {
// ...
}
Tenga en cuenta que cuando su método de acción solo devuelve una cadena, también puede especificar exactamente esa cadena en el action
atributo. Por lo tanto, esto es totalmente torpe:
<h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />
Con este método sin sentido que devuelve una cadena codificada:
public String goToNextpage() {
return "nextpage";
}
En cambio, solo ponga esa cadena codificada directamente en el atributo:
<h:commandLink value="Go to next page" action="nextpage" />
Tenga en cuenta que esto a su vez indica un mal diseño: navegar por POST. Esto no es fácil de usar ni SEO. Todo esto se explica en ¿ Cuándo debo usar h: outputLink en lugar de h: commandLink? y se supone que debe resolverse como
<h:link value="Go to next page" outcome="nextpage" />
Consulte también ¿Cómo navegar en JSF? Cómo hacer que la URL refleje la página actual (y no la anterior) .
Desde JSF 2.x hay una tercera vía, la <f:ajax listener>
.
<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
El ajaxListener
método tiene por defecto la siguiente firma:
import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
En Mojarra, el AjaxBehaviorEvent
argumento es opcional, a continuación funciona igual de bien.
public void ajaxListener() {
// ...
}
Pero en MyFaces, arrojaría un MethodNotFoundException
. A continuación funciona en ambas implementaciones JSF cuando desea omitir el argumento.
<h:commandXxx ...>
<f:ajax execute="@form" listener="#{bean.ajaxListener()}" render="@form" />
</h:commandXxx>
Los oyentes Ajax no son realmente útiles en los componentes del comando. Son más útiles en la entrada y seleccionan componentes <h:inputXxx>
/ <h:selectXxx>
. En los componentes de comando, simplemente manténgase action
y / o actionListener
para mayor claridad y mejor código de autodocumentación. Además, al igual que actionListener
, f:ajax listener
no admite la devolución de un resultado de navegación.
<h:commandXxx ... action="#{bean.action}">
<f:ajax execute="@form" render="@form" />
</h:commandXxx>
Para obtener explicaciones execute
y render
atributos, diríjase a Comprensión del proceso / actualización de PrimeFaces y JSF f: ajax ejecutar / renderizar atributos .
Los actionListener
s siempre se invocan antes de la action
en el mismo orden en que se han declarado en la vista y se unen al componente. El f:ajax listener
siempre se invoca antes de cualquier escucha de acción. Entonces, el siguiente ejemplo:
<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>
Invocará los métodos en el siguiente orden:
Bean#ajaxListener()
Bean#actionListener()
ActionListenerType#processAction()
Bean#actionListenerBinding()
Bean#setProperty()
Bean#action()
El actionListener
admite una excepción especial: AbortProcessingException
. Si se lanza esta excepción desde un actionListener
método, JSF omitirá cualquier escucha de acción restante y el método de acción y procederá a procesar la respuesta directamente. No verá una página de error / excepción, sin embargo, JSF lo registrará. Esto también se hará implícitamente cuando se produzca cualquier otra excepción desde unactionListener
. Entonces, si tiene la intención de bloquear la página por una página de error como resultado de una excepción comercial, entonces definitivamente debe realizar el trabajo en el action
método.
Si la única razón para usar un actionListener
es que un void
método regrese a la misma página, entonces ese es uno malo. Los action
métodos también pueden regresar perfectamente void
, al contrario de lo que algunos IDEs le permiten creer a través de la validación EL. Tenga en cuenta que los ejemplos de escaparate de PrimeFaces están llenos de este tipo de actionListener
s en todo lugar. Esto de hecho está mal. No uses esto como una excusa para hacerlo tú mismo.
En las solicitudes ajax, sin embargo, se necesita un controlador de excepción especial. Esto es independientemente de si usa el listener
atributo de <f:ajax>
o no. Para obtener una explicación y un ejemplo, diríjase a Manejo de excepciones en solicitudes JSF ajax .
actionListener
, pero eso aún no es una buena excusa para abusar actionListener
de las acciones comerciales .
action
corresponde con eso. actionListener
es para cosas secundarias. Solo quería aclarar que las excepciones de actionListener
s pueden propagarse si es necesario;)
actionListener
atributo y también debe serlo public
. El processAction
nombre solo es obligatorio cuando lo usa <f:actionListener type>
, simplemente porque el tipo tiene que implementar una ActionListener
interfaz que tenga exactamente ese nombre de método processAction
definido.
<f:ajax>
, en el caso de componentes de comando preferiría usar el action
atributo para acciones comerciales. Por ej <h:commandButton action="#{bean.businessAction}"><f:ajax/></h:commandButton>
.
Como BalusC indicó, actionListener
por defecto se traga excepciones, pero en JSF 2.0 hay un poco más de esto. Es decir, no solo se traga y registra, sino que en realidad publica la excepción.
Esto sucede a través de una llamada como esta:
context.getApplication().publishEvent(context, ExceptionQueuedEvent.class,
new ExceptionQueuedEventContext(context, exception, source, phaseId)
);
El oyente predeterminado para este evento es el ExceptionHandler
que para Mojarra está configurado encom.sun.faces.context.ExceptionHandlerImpl
. Esta implementación básicamente volverá a generar cualquier excepción, excepto cuando se trata de una excepción AbortProcessingException, que se registra. Los ActionListeners envuelven la excepción que arroja el código del cliente en una AbortProcessingException que explica por qué siempre se registran.
Sin ExceptionHandler
embargo, esto se puede reemplazar en faces-config.xml con una implementación personalizada:
<exception-handlerfactory>
com.foo.myExceptionHandler
</exception-handlerfactory>
En lugar de escuchar globalmente, un solo bean también puede escuchar estos eventos. Lo siguiente es una prueba de concepto de esto:
@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");
}
}
(tenga en cuenta que no es así como se deben codificar normalmente los oyentes, ¡esto es solo para fines de demostración!)
Llamando a esto desde un Facelet como este:
<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>
Resultará en una página de error que se muestra.
ActionListener se dispara primero, con una opción para modificar la respuesta, antes de que se llame a Action y determine la ubicación de la página siguiente.
Si tiene varios botones en la misma página que deberían ir al mismo lugar pero hacer cosas ligeramente diferentes, puede usar la misma Acción para cada botón, pero use un ActionListener diferente para manejar funcionalidades ligeramente diferentes.
Aquí hay un enlace que describe la relación:
TL; DR :
Los ActionListener
s (pueden ser múltiples) se ejecutan en el orden en que se registraron ANTES deaction
Respuesta larga :
Una empresa action
generalmente invoca un servicio EJB y, si es necesario, también establece el resultado final y / o navega a una vista diferente si eso no es lo que está haciendo y actionListener
es más apropiado, es decir, cuando el usuario interactúa con los componentes, como h:commandButton
o h:link
pueden se maneja pasando el nombre del método de bean administrado en el actionListener
atributo de un Componente UI o para implementar una ActionListener
interfaz y pasar el nombre de la clase de implementación al actionListener
atributo de un Componente UI.