8

I want to use multiple action listener to set state of two backing beans before further processing

1st way:

<p:commandButton process="@this" >
   <f:attribute name="key" value="#{node.getIdTestGroup()}" />
   <f:actionListener binding="#{testController.nodeListener}" />
<f:actionListener binding="#{testDeviceGroupController.prepareCreate}" />
</p:commandButton>

It give an exception:

WARNING: /testGroup/List.xhtml @26,88 binding="#{testController.nodeListener()}": Method nodeListener not found javax.el.ELException: /testGroup/List.xhtml @26,88 binding="#{testController.nodeListener()}": Method nodeListener not found

2nd way:

<p:commandButton process="@this" >
    <f:attribute name="key" value="#{node.getIdTestGroup()}" />
    <f:actionListener binding="#{testController.nodeListener(event)}" />
    <f:actionListener binding="#{testDeviceGroupController.prepareCreate(event)}" />
</p:commandButton>

Event is null on the nodeListener and prepareCreate methods

How to do it correct?

smolarek999
  • 509
  • 1
  • 5
  • 21
  • 2
    Related: http://stackoverflow.com/questions/3909267/differences-between-action-and-actionlistener/3909382#3909382 – BalusC May 04 '13 at 23:23

2 Answers2

13

I see you facilitate the traditional approach of guess-how-it-works-using-bare-intuition-and-random-associations-then-act-surprised :-)

f:actionListener only lets you add a whole object as an observer, not an arbitrary method. You can either use type attribute to specify the class name (it will be instantiated by JSF) or binding attribute to give an instance of the object that you created by yourself (not a method!). The object must implement javax.faces.event.ActionListener.

Your second try (testDeviceGroupController.prepareCreate(event)) is wrong on many levels, but the crux is that the methods are called not to handle your action, but to create the Actionlistener instance.

You have a couple of options:

  • the sanest one: just make a method that calls each of the target methods. Since they are on different beans, you can inject one into the other.
  • if that doesn't work for you, you can create a method that creates a listener object.

Like this:

public ActionListener createActionListener() {
    return new ActionListener() {
        @Override
        public void processAction(ActionEvent event) throws AbortProcessingException {
            System.out.println("here I have both the event object, and access to the enclosing bean");
        }
    };
}

and use it like this:

<h:commandButton>
    <f:actionListener binding="#{whateverBean.createActionListener()}"/>            
</h:commandButton>
fdreger
  • 12,264
  • 1
  • 36
  • 42
  • To make it clear, why * working ? ( I used it before when I need only onebean ) It create listener object out-of-the-box and than call whateverBean.actionListenerMethod ? – smolarek999 May 04 '13 at 13:18
  • 1
    @smolarek999: it works, because it is supposed to work like this and it is stated in the documentation! commandbutton's actionListenerMethod takes an EL of a method to call; and actionlisteners binding takes an EL of an ActionListener object. You cannot just guess that two things work the same just because the name is similar. Really, guessing is a bad, bad idea. – fdreger May 04 '13 at 17:02
  • 1
    ROFL: the traditional approach of guess-how-it-works-using-bare-intuition-and-random-associations-then-act-surprised – Christian Beikov Oct 29 '14 at 12:29
0

The binding attribute value needs to point to an object implementing ActionListener interface, not a method.

From the documentation of f:actionListener's bindig attribute :

Value binding expression that evaluates to an object that implements javax.faces.event.ActionListener.

A similar problem was discussed here.

Community
  • 1
  • 1
dratewka
  • 2,104
  • 14
  • 15