Множество исполнителей действий в JSF

Я хочу использовать несколько исполнителей действий для установки состояния двух резервных копий beans перед дальнейшей обработкой

Первый способ:

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

Это дает исключение:

ПРЕДУПРЕЖДЕНИЕ:/testGroup/List.xhtml @26,88 binding = "# {testController.nodeListener()}": метод nodeListener не найден javax.el.ELException:/testGroup/List.xhtml @26,88 binding = "# {testController.nodeListener()}": метод nodeListener не найден

Второй способ:

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

Событие равно null для методов nodeListener и prepareCreate

Как это сделать правильно?

Ответ 1

Я вижу, что вы облегчаете традиционный подход догадки-как-он-работает-используя-bare-intuition-and-random-association-then-act-surprise: -)

f:actionListener позволяет вам добавлять весь объект в качестве наблюдателя, а не произвольный метод. Вы можете использовать атрибут type, чтобы указать имя класса (он будет создан с помощью JSF) или атрибут binding, чтобы предоставить экземпляр созданного вами объекта (а не метода!). Объект должен реализовать javax.faces.event.ActionListener.

Вторая попытка (testDeviceGroupController.prepareCreate(event)) неверна на многих уровнях, но суть в том, что методы вызываются не для обработки вашего действия, а для создания экземпляра Actionlistener.

У вас есть несколько вариантов:

  • Самый простой: просто создайте метод, который вызывает каждый из целевых методов. Поскольку они находятся на разных beans, вы можете вводить один в другой.
  • Если это не сработает для вас, вы можете создать метод, создающий объект-слушатель.

Вот так:

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");
        }
    };
}

и используйте его следующим образом:

<h:commandButton>
    <f:actionListener binding="#{whateverBean.createActionListener()}"/>            
</h:commandButton>

Ответ 2

Значение атрибута binding должно указывать на объект, реализующий интерфейс ActionListener, а не на метод.

Из документации атрибута f:actionListener bindig:

Выражение привязки значений, которое оценивается объектом, реализующим javax.faces.event.ActionListener.

Аналогичная проблема обсуждалась здесь.