JSF2: action and actionListener

29,248

Solution 1

You're confusing action with actionListener. The actionListener runs always before the action. If there are multiple action listeners, then they run in the same order as they have been registered. That's why it doesn't work as expected when you use actionListener to call the business action and <f:setPropertyActionListener> to set (prepare) a property which is to be used by the business action. This problem was pointed out and fixed in your previous question Is this Primefaces bug or Mojarra/MyFaces bug.

Whatever you have in the delete() method is clearly a business action and should be invoked by action instead. A business action typically invokes an EJB service and if necessary also sets the final result and/or navigates to a different view.

Solution 2

I tried your example with original JSF's tags <h:commandButton> but I also get the same symptom. I believe if you specify actionListener attribute and at the same time, declare another listener with <f:setPropertyActionListener>, the listener in the attribute actionListener will be fired before the other.

UPDATE: I test my assumption with the following code:

  • Change your delete function to this one:

    public void delete(){
        this.selectedFood = "Chicken";
        //foodList.remove(selectedFood);
    }
    
  • Add <h:outputText id="food" value="#{viewBean.selectedFood}" /> inside <h:panelGroup id="mygroup">.

You will see that the outputText is always Chicken.

Share:
29,248
Thang Pham
Author by

Thang Pham

Updated on January 01, 2020

Comments

  • Thang Pham
    Thang Pham over 4 years

    From this answer by BalusC here Differences between action and 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>,. However when I decide to write some code to test this, the result is a bit different. Here is my small code

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

    Here is my 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...
    }
    

    According to BalusC, actionListener is more suitable here, but my example show otherwise.

    The above code work great with action, but if I switch over to actionListener, then it does not quite work. It will take two clicks for me to delete an entry of this table using actionListener, while if I use action, it delete entry every time I click the button. I wonder if any JSF expert out there can help me understand action vs actionListener

    Note If I switch to actionListener, my delete method become public void delete(ActionEvent actionEvent)