Diferencias entre action y ActionListener


¿Cuál es la diferencia entre action y actionListener, y cuando debo usar action versus actionListener?

Author: BalusC, 2010-10-11

3 answers

ActionListener

Use actionListener si desea tener un hook antes de que se ejecute la acción de negocio real, por ejemplo, para registrarla, 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, puramente con fines de preparación antes de que se invoque la acción real del negocio.

El método actionListener 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 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 argumento ActionEvent 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) {}

Nótese la importancia de los paréntesis en la expresión método argumentless. Si estuvieran ausentes, JSF todavía esperaría un método con argumento ActionEvent.

Si estás en EL 2.2+, luego puedes declarar múltiples métodos de escucha de acciones 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 binding. Si estuvieran ausentes, EL lanzaría confusamente un javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean, porque el atributo binding 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 + transparentemente convierte una expresión de valor en una expresión de método. Véase también a. o. ¿Por qué soy capaz de atar a un método arbitrario si no es compatible con JSF?


Medidas

Use action si desea ejecutar una acción de negocio y si es necesario manejar la navegación. El método action puede (por lo tanto, no debe) devolver un String que se utilizará como resultado de caso de navegación (la vista de destino). Un valor devuelto de null o void le permitirá regresar a la misma página y mantener vivo el ámbito de vista actual. Un valor devuelto de una cadena vacía o el mismo ID de vista también vuelva a la misma página, pero vuelva a crear el ámbito de vista y, por lo tanto, destruya todos los frijoles con el ámbito de vista actualmente activos y, si corresponde, vuelva a crearlos.

El método action puede ser cualquier 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, entonces también puede especificar exactamente esa cadena en el atributo action. Por lo tanto, esto es totalmente torpe:

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

Con este método sin sentido devolviendo una cadena hardcoded:

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 CORREO. Esto no es amigable con el usuario ni con el SEO. Todo esto se explica en ¿Cuándo debo usar h:outputLink en lugar de h:commandLink? y se supone que se resuelve, como

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

Véase 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 vía, la <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 igual de bien.

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

Pero en mis caras, lanzaría un MethodNotFoundException. A continuación funciona en ambas implementaciones de JSF cuando se desea omitir el argumento.

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

Oyentes de Ajax no son realmente útiles en componentes de comando. Son más útiles en la entrada y seleccionar componentes <h:inputXxx>/<h:selectXxx>. En los componentes de comando, simplemente se adhieren a action y/o actionListener para mayor claridad y mejor código de auto-documentación. Además, al igual que actionListener, el f:ajax listener no admite devolver un resultado de navegación.

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

Para una explicación sobre los atributos execute y render, diríjase a Entendiendo el proceso/actualización de PrimeFaces y JSF f: ajax execute / render atributos.


Orden de invocación

Los actionListener s siempre se invocan antes de los action en el mismo orden en que se declaran en la vista y se adjuntan al componente. El f:ajax listener siempre se invoca antes de cualquier escucha de acciones. Por lo tanto, 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 de la siguiente manera orden:

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

Manejo de excepciones

El actionListener admite una excepción especial: AbortProcessingException. Si esta excepción es lanzada desde un método actionListener, entonces JSF saltará cualquier oyente de acción restante y el método de acción y procederá a renderizar la respuesta directamente. No verá una página de error/excepción, JSF sin embargo lo registrará. Este 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 de negocio, entonces definitivamente debería realizar el trabajo en el método action.

Si la única razón para usar un actionListener es tener un método void volviendo a la misma página, entonces eso es malo. Los métodos action también pueden devolver perfectamente void, al contrario de lo que algunos IDE le permiten creer a través de la validación EL. Tenga en cuenta que los ejemplos de PrimeFaces showcase están llenos de este tipo de actionListeners sobre todos los lugares. Esto es un error. No uses esto como excusa para hacerlo tú mismo.

En las peticiones ajax, sin embargo, se necesita un controlador de excepciones especial. Esto es independientemente de si se utiliza listener atributo de <f:ajax> o no. Para una explicación y un ejemplo, diríjase a Exception handling in JSF ajax requests.

 535
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:47:26

Como indicó BalusC, el actionListener por defecto 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 receptor predeterminado para este evento es el ExceptionHandler que para Mojarra se establece en com.sun.faces.context.ExceptionHandlerImpl. Esta implementación básicamente repensará cualquier excepción, excepto cuando se trate de una excepción AbortProcessingException, que se registra. ActionListeners envuelve la excepción que es lanzada por el código del cliente en una excepción de este tipo, AbortProcessingException, lo que explica por qué siempre se registran.

Esto ExceptionHandler puede ser reemplazado sin embargo en faces-config.xml con una implementación personalizada:

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

En lugar de escuchar globalmente, un solo frijol 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");
    }

}

(nota, esto no es como uno debería normalmente codificar oyentes, esto es solo para ¡propósitos de demostración!)

Llamando a esto desde una Faceta como esta:

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

Hará que se muestre una página de error.

 44
Author: Arjan Tijms,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-19 14:40:50

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 deben 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 una funcionalidad ligeramente diferente.

Aquí hay un enlace que describe la relación:

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

 37
Author: Erick Robertson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-11 19:38:54