JSF2: действие и действиеListener

Из этого ответа от BalusC здесь Различия между действием и 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>,. Однако, когда я решаю написать какой-то код для проверки этого, результат немного отличается. Вот мой небольшой код

<h:form id="form"> 
   <h:panelGroup id="mygroup">
     <p:dataTable id="mytable" value="#{viewBean.foodList}" var="item">
         <p:column>
             #{item}
         </p:column>
         <p:column>
             <p:commandButton value="delete" 
                        action="#{viewBean.delete}"
                        update=":form:mygroup">
                 <f:setPropertyActionListener target="#{viewBean.selectedFood}"
                                              value="#{item}"/>
             </p:commandButton>
         </p:column>
      </p:dataTable>
   </h:panelGroup>
</h:form>

Вот мой bean

@ManagedBean
@ViewScoped
public class ViewBean {
    private List<String> foodList;
    private String selectedFood;

    @PostConstruct
    public void init(){

        foodList = new ArrayList<String>();
        foodList.add("Pizza");
        foodList.add("Pasta");
        foodList.add("Hamburger");
    }

    public void delete(){
        foodList.remove(selectedFood);
    }
    //setter, getter...
}

Согласно BalusC, actionListener здесь более подходит, но в моем примере показано иначе.

Приведенный выше код отлично работает с action, но если я переключусь на actionListener, значит, он не работает. Мне потребуется два клика, чтобы удалить запись этой таблицы с помощью actionListener, а если я использую action, она удаляет запись каждый раз, когда я нажимаю кнопку. Интересно, может ли какой-нибудь эксперт JSF помочь мне понять action vs actionListener

Примечание Если я переключусь на actionListener, мой метод delete станет public void delete(ActionEvent actionEvent)

Ответ 1

Вы вводите в заблуждение action с помощью actionListener. actionListener всегда работает перед action. Если есть несколько прослушивателей действий, они запускаются в том же порядке, в каком они были зарегистрированы. Поэтому он не работает так, как ожидалось, когда вы используете actionListener для вызова бизнес-действия и <f:setPropertyActionListener> для установки (подготовки) свойства, которое должно использоваться бизнес-действием. Эта проблема была указана и исправлена ​​в вашем предыдущем вопросе Является ли это ошибкой Primefaces или ошибкой Mojarra/MyFaces.

Независимо от того, что у вас есть в методе delete(), очевидно, является бизнес-действием и вместо него следует использовать action. Бизнес-действие обычно вызывает службу EJB, а при необходимости также устанавливает конечный результат и/или переходит к другому виду.

Ответ 2

Я попробовал ваш пример с оригинальными тегами JSF <h:commandButton>, но я тоже получаю тот же симптом. Я считаю, что если вы укажете атрибут actionListener и в то же время объявите другого слушателя с помощью <f:setPropertyActionListener>, слушатель в атрибуте actionListener будет запущен перед другим.

UPDATE. Я проверяю свое предположение следующим кодом:

  • Измените функцию delete на следующую:

    public void delete(){
        this.selectedFood = "Chicken";
        //foodList.remove(selectedFood);
    }
    
  • Добавить <h:outputText id="food" value="#{viewBean.selectedFood}" /> внутри <h:panelGroup id="mygroup">.

Вы увидите, что outputText всегда Chicken.