Kevin
Kevin

Reputation: 4128

Can't get MenuItem to have its ActionListeners called

I am trying to get a Primefaces MenuItem [within a Menubar] to emit an ActionEvent and call a method when pressed, however, the listening methods are never called, and I have no idea why:

xhtml page:

<h:body>
<h:form>
    <p:menubar model="#{testBean.model}" />
    <h:commandButton value="submit" />
    <h:outputText value="#{testBean.confirmation}" />
</h:form>
</h:body>
</html>

jsf bean:

@ManagedBean
public class TestBean implements ActionListener{

    MenuModel model = new DefaultMenuModel();
    private String confirmation = "negative";

    public TestBean() {
        MenuItem item = new MenuItem();
        item.setValue("Click");
        item.setUrl("#");
        item.addActionListener(this);
        model.addMenuItem(item);
    }

    @Override
    public void processAction(ActionEvent event)
            throws AbortProcessingException {

        // EXECUTION NEVER REACHES HERE!
        confirmation = "positive";
    }

    public MenuModel getModel() {
        return model;
    }

    public String getConfirmation() {
        return confirmation;
    }
}

It might help to visualise the very simple output:

enter image description here

I'm using Primefaces 3.1.0, and JSF 2.0

Upvotes: 2

Views: 8938

Answers (4)

Kevin
Kevin

Reputation: 4128

The first problem with the original code was that I needed a unique Id. I discovered this early on, but it lead to another very annoying issues: the action listener would be called, but the page would not update. To solve this issue (as described here), you either disable ajax on the MenuItem or customise how it updates.

The correct constructor code is:

public TestBean() {
    MenuItem item = new MenuItem();
    item.setValue("Click");

    // New code:
    item.setId(FacesContext.getCurrentInstance().getViewRoot().createUniqueId());
    item.setAjax(false);

    item.setUrl("#");
    item.addActionListener(this);
    model.addMenuItem(item);
}

Upvotes: 1

Olofu Mark
Olofu Mark

Reputation: 19

@Mogwli, item.setId((new UiViewRoot()).createUniqueId()); will cause an IllegalArgumentException since new UIViewRoot() will not be context aware... Should have been item.setId(FacesContext.getCurrentInstance().getViewRoot().createUniqueId());

Upvotes: 1

maple_shaft
maple_shaft

Reputation: 10463

First of all, your code snippet shows an <h:commandButton> which is not the Primefaces commandButton <p:commandButton>.

Secondly, you are not specifying the actionListener attribute of the commandButton to point to processAction.

<p:commandButton value="submit" actionListener="#{testBean.processAction}" /> 

Upvotes: 0

Tim Br&#252;ckner
Tim Br&#252;ckner

Reputation: 2099

My first suggestion is to add some kind of debug logging to your action listener in order to find out, whether it got called or not. If it gets called but your site isn't rendering the changes it might be due to the following reasons.

  • The action listener does not trigger an update of the client side DOM.
  • The submit button triggers a request. The managed bean gets pulled up and reinitialized with default values, since it gets managed in a request scope. You should try putting your bean into @ViewScoped or @SessionScoped management.

By the way, we do not implement ActionListener. We assign method expressions to the menu items. Here is a short mockup example: We used an action of a menu item to toggle button states of a context menu. Hope I could help you somehow.

Facelet snippet:

<p:menu model="#{menuBean.model}" />

Bean snippet:

@ManagedBean
@ViewScoped
public class MenuBean implements Serializable {

    private MenuModel model;
    private boolean cutAllowed = false;

    /** Creates a new instance of MenuBean */
    public MenuBean() {
        model = new DefaultMenuModel();

        //First submenu
        Submenu submenu = new Submenu();
        submenu.setLabel("Dynamic Submenu 1");

        MenuItem item = new MenuItem();
        item.setValue("toggle");
        item.setUpdate("contextMenu");
        item.setAjax(true);
        item.setIcon("ui-icon ui-icon-disk");
        ExpressionFactory factory = FacesContext.getCurrentInstance().getApplication().getExpressionFactory();
        MethodExpression methodExpression = factory.createMethodExpression(FacesContext.getCurrentInstance().getELContext(), "#{menuBean.toggleCutState}", String.class, new Class[]{});

        item.setActionExpression(methodExpression);

        submenu.getChildren().add(item);

        model.addSubmenu(submenu);
    }

    public boolean isCutAllowed() {
        return cutAllowed;
    }

    public void setCutAllowed(boolean cutAllowed) {
        this.cutAllowed = cutAllowed;
    }

    public String toggleCutState() {
        toggleCutState(null);
        return null;
    }

    public void toggleCutState(ActionEvent event) {
        this.cutAllowed = !this.cutAllowed;
        System.out.println("toggle called");
    }       

    public MenuModel getModel() {
        return model;
    }  
}

Upvotes: 3

Related Questions