Differences between action and actionListener

Murat Güzel picture Murat Güzel · Oct 11, 2010 · Viewed 262.1k times · Source

What is the difference between action and actionListener, and when should I use action versus actionListener?

Answer

BalusC picture BalusC · Oct 11, 2010

actionListener

Use actionListener if you want have a hook before the real business action get executed, e.g. to log it, and/or to set an additional property (by <f:setPropertyActionListener>), and/or to have access to the component which invoked the action (which is available by ActionEvent argument). So, purely for preparing purposes before the real business action gets invoked.

The actionListener method has by default the following signature:

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

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

And it's supposed to be declared as follows, without any method parentheses:

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

Note that you can't pass additional arguments by EL 2.2. You can however override the ActionEvent argument altogether by passing and specifying custom argument(s). The following examples are valid:

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

Note the importance of the parentheses in the argumentless method expression. If they were absent, JSF would still expect a method with ActionEvent argument.

If you're on EL 2.2+, then you can declare multiple action listener methods via <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() {}

Note the importance of the parentheses in the binding attribute. If they were absent, EL would confusingly throw a javax.el.PropertyNotFoundException: Property 'actionListener1' not found on type com.example.Bean, because the binding attribute is by default interpreted as a value expression, not as a method expression. Adding EL 2.2+ style parentheses transparently turns a value expression into a method expression. See also a.o. Why am I able to bind <f:actionListener> to an arbitrary method if it's not supported by JSF?


action

Use action if you want to execute a business action and if necessary handle navigation. The action method can (thus, not must) return a String which will be used as navigation case outcome (the target view). A return value of null or void will let it return to the same page and keep the current view scope alive. A return value of an empty string or the same view ID will also return to the same page, but recreate the view scope and thus destroy any currently active view scoped beans and, if applicable, recreate them.

The action method can be any valid MethodExpression, also the ones which uses EL 2.2 arguments such as below:

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

With this method:

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

Note that when your action method solely returns a string, then you can also just specify exactly that string in the action attribute. Thus, this is totally clumsy:

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

With this senseless method returning a hardcoded string:

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

Instead, just put that hardcoded string directly in the attribute:

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

Please note that this in turn indicates a bad design: navigating by POST. This is not user nor SEO friendly. This all is explained in When should I use h:outputLink instead of h:commandLink? and is supposed to be solved as

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

See also How to navigate in JSF? How to make URL reflect current page (and not previous one).


f:ajax listener

Since JSF 2.x there's a third way, the <f:ajax listener>.

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

The ajaxListener method has by default the following signature:

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

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

In Mojarra, the AjaxBehaviorEvent argument is optional, below works as good.

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

But in MyFaces, it would throw a MethodNotFoundException. Below works in both JSF implementations when you want to omit the argument.

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

Ajax listeners are not really useful on command components. They are more useful on input and select components <h:inputXxx>/<h:selectXxx>. In command components, just stick to action and/or actionListener for clarity and better self-documenting code. Moreover, like actionListener, the f:ajax listener does not support returning a navigation outcome.

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

For explanation on execute and render attributes, head to Understanding PrimeFaces process/update and JSF f:ajax execute/render attributes.


Invocation order

The actionListeners are always invoked before the action in the same order as they are been declared in the view and attached to the component. The f:ajax listener is always invoked before any action listener. So, the following example:

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

Will invoke the methods in the following order:

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

Exception handling

The actionListener supports a special exception: AbortProcessingException. If this exception is thrown from an actionListener method, then JSF will skip any remaining action listeners and the action method and proceed to render response directly. You won't see an error/exception page, JSF will however log it. This will also implicitly be done whenever any other exception is being thrown from an actionListener. So, if you intend to block the page by an error page as result of a business exception, then you should definitely be performing the job in the action method.

If the sole reason to use an actionListener is to have a void method returning to the same page, then that's a bad one. The action methods can perfectly also return void, on the contrary to what some IDEs let you believe via EL validation. Note that the PrimeFaces showcase examples are littered with this kind of actionListeners over all place. This is indeed wrong. Don't use this as an excuse to also do that yourself.

In ajax requests, however, a special exception handler is needed. This is regardless of whether you use listener attribute of <f:ajax> or not. For explanation and an example, head to Exception handling in JSF ajax requests.