jsf - etiquetas - Diferencias entre acción y acciónListener
jsf etiquetas (3)
¿Cuál es la diferencia entre action
y actionListener
, y cuándo debo usar action
versus actionListener
?
actionListener
Use actionListener
si desea tener un gancho antes de que se ejecute la acción de negocio real, por ejemplo, para registrarlo y / o para establecer una propiedad adicional (por <f:setPropertyActionListener>
), y / o para tener acceso al componente que invocó el acción (que está disponible por el argumento ActionEvent
). Por lo tanto, solo para fines de preparación antes de que se invoque la acción empresarial real.
El método actionListener
tiene por defecto la siguiente firma:
import javax.faces.event.ActionEvent;
// ...
public void actionListener(ActionEvent event) {
// ...
}
Y se supone que se declara 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 por completo el argumento ActionEvent
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 todavía esperaría un método con el argumento ActionEvent
.
Si está en EL 2.2+, puede declarar múltiples 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 atributo de binding
. Si estuvieran ausentes, EL lanzaría confusamente una javax.el.PropertyNotFoundException: Property ''actionListener1'' not found on type com.example.Bean
, porque el atributo de binding
se interpreta por defecto como una expresión de valor, no como una expresión de método. Añadir paréntesis de estilo EL 2.2+ de forma transparente convierte una expresión de valor en una expresión de método. Ver también ao ¿Por qué puedo enlazar <f: actionListener> a un método arbitrario si no está soportado por JSF?
acción
Use action
si desea ejecutar una acción comercial y, si es necesario, manejar la navegación. El método de action
puede (por lo tanto, no debe) devolver una String
que se utilizará como resultado del caso de navegación (la vista de destino). Un valor de retorno de null
o void
permitirá que regrese a la misma página y mantenga vivo el alcance de la vista actual. Un valor de retorno de una cadena vacía o el mismo ID de vista también volverá a la misma página, pero volverá a crear el alcance de la vista y, por lo tanto, destruirá los frijoles con ámbito de vista actualmente activos y, si corresponde, los recreará.
El método de action
puede ser cualquier MethodExpression
válido, también los que usan argumentos de 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, entonces también puede especificar exactamente esa cadena en el atributo de action
. Por lo tanto, esto es totalmente torpe:
<h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />
Con este método sin sentido devolver una cadena codificada:
public String goToNextpage() {
return "nextpage";
}
En su lugar, simplemente 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 usuario ni SEO amigable. Todo esto se explica en ¿ Cuándo debo usar h: outputLink en lugar de h: commandLink? y se supone que debe ser resuelto como
<h:link value="Go to next page" outcome="nextpage" />
Vea también ¿Cómo navegar en JSF? Cómo hacer que la URL refleje la página actual (y no la anterior) .
f: oyente ajax
Desde JSF 2.x hay una tercera forma, el <f:ajax listener>
.
<h:commandXxx ...>
<f:ajax listener="#{bean.ajaxListener}" />
</h:commandXxx>
El método ajaxListener
tiene por defecto la siguiente firma:
import javax.faces.event.AjaxBehaviorEvent;
// ...
public void ajaxListener(AjaxBehaviorEvent event) {
// ...
}
En Mojarra, el argumento AjaxBehaviorEvent
es opcional, a continuación funciona como bueno.
public void ajaxListener() {
// ...
}
Pero en MyFaces, lanzaría una 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 escuchas Ajax no son realmente útiles en los componentes de comando. Son más útiles en los componentes de entrada y selección <h:inputXxx>
/ <h:selectXxx>
. En los componentes de comando, simplemente apégate a action
y / o actionListener
para mayor claridad y mejor código de auto-documentación. Además, como actionListener
, el 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 una explicación sobre los atributos de execute
y render
, diríjase a Entender los procesos / actualización de PrimeFaces y JSF f: ajax execute / render .
Orden de invocación
Los actionListener
s siempre se invocan antes de la action
en el mismo orden en que se declararon en la vista y se adjuntaron al componente. El f:ajax listener
siempre se invoca antes que cualquier escucha de acción. Así, 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()
Manejo de excepciones
El actionListener
admite una excepción especial: AbortProcessingException
. Si esta excepción se produce desde un método de actionListener
, JSF omitirá los escuchas de acción restantes y el método de acción y procederá a presentar la respuesta directamente. No verá una página de error / excepción, sin embargo, JSF lo registrará. Esto también se hará implícitamente cada vez que se lance cualquier otra excepción desde un actionListener
. Por lo tanto, si tiene la intención de bloquear la página por una página de error como resultado de una excepción empresarial, entonces definitivamente debe realizar el trabajo en el método de action
.
Si la única razón para usar un actionListener
es tener un método void
regresando a la misma página, entonces esa es una mala idea. Los métodos de action
también pueden devolverse perfectamente, al contrario de lo que algunos IDE le permiten creer mediante la validación EL. Tenga en cuenta que los ejemplos de muestra de PrimeFaces están llenos de este tipo de actionListener
s en todo lugar. Esto está realmente mal. No uses esto como una excusa para hacer eso tú mismo.
Sin embargo, en las solicitudes ajax, se necesita un controlador de excepción especial. Esto es independiente de si utiliza el atributo de listener
de <f:ajax>
o no. Para obtener una explicación y un ejemplo, diríjase al manejo de excepciones en las solicitudes ajax de JSF .
Como indicó BalusC, el actionListener
por defecto se traga las 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 se establece en com.sun.faces.context.ExceptionHandlerImpl
. Básicamente, esta implementación volverá a generar cualquier excepción, excepto cuando se trate de una excepción AbortProcessingException, que se registra. Los ActionListeners envuelven la excepción que es lanzada por el código del cliente en tal AbortProcessingException que explica por qué siempre se registran.
Sin embargo, este ExceptionHandler
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. La 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 uno debería codificar normalmente a los oyentes, ¡esto es solo para fines de demostración!)
Llamando 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 activa 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 hacen cosas ligeramente diferentes, puede usar la misma Acción para cada botón, pero use un ActionListener diferente para manejar una funcionalidad ligeramente diferente.
Aquí hay un enlace que describe la relación: