简体   繁体   中英

<h:commandButton> does not invoke action

During some tests made using JSF 2 (mojarra on Glassfish 3.1.1) I've faced with strange behavior I can't explain.

This is my managed bean:

@ManagedBean
@RequestScoped
public class TestBean {
    private int id;

    public void hideButton() {
        id = 0;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

Here is my xhtml page

  <h:form> <h:inputHidden value="#{testBean.id}"/> <h:outputText value="#{testBean.id}"/> <h:commandButton value="set 1" actionListener="#{testBean.setId(1)}"> </h:commandButton> <h:commandButton value="hide button" action="#{testBean.hideButton}" rendered="#{testBean.id > 0}"> </h:commandButton> </h:form> 

I expected, the button "hide button" is not visible on initial load of the page and this is really the fact. After a click on the button "set 1", the button "hide button" appeares and that is also to expect. Really not understandable thing to me is the fact the subsequent click on the button "hide button" does not invoke the method testBean.hideButton and set the id to 0. I've read the very useful answer from BalusC (thanks a lot really) here

commandButton/commandLink/ajax action/listener method not invoked or input value not updated

and recognize, the problem is related to the attribute "rendered", if I remove it, the action is invoked. But as far as I aware, the class member must be initialized during the UPDATE MODEL VALUES phase and the condition mentioned in the attribute rendered should be evaluated to true during INVOKE APPLICATION phase and the action should be invoked.

The example works if I change the scope of the bean to View/Session. But it also works fine if I remove the render attribute from the "hide button"

Would somebody explain such behavior?

In other words, at what phase the expression of the rendered attribute is evaluated to make decision not to invoke the action?

The rendered attribute is also evaluated during apply request values phase, at the moment when JSF needs to identify which action needs to be invoked. If it evaluates false , then the action can't be identified and will thus also not be invoked.

The problem is caused by the too narrow managed bean scope. As your managed bean is request scoped, it get trashed by end of response and recreated (with all properties set to default) on any subsequent request. The model value on which the rendered attribute depends will only be updated during update model values phase, which is too late. You should be placing the managed bean in view scope instead.

Apart from changing the bean scope to view scope, another way is to check the request parameter map value instead in the rendered attribute.

<h:form id="form">
    <h:inputHidden id="id" ... />

    <h:commandButton ... rendered="#{param['form:id'] gt 0}" />
</h:form>

(by the way your usage of > instead of gt indicates that you're using the deprecated JSP view technology instead of Facelets, I would strongly recommend to migrate to Facelets)

See also:

I've found myself the reason of the problem.

The method UIComponentBase.processDecodes (at the Apply Request Values phase) calls isRendered, which returns false, because it is before the Update Model Values. Which skips the decode of the component.

There are some workarounds possible, all of them imho are not cool, but nevertheless it works

It is possible manually to set necessary value in managed bean in (post)constructor from request parameters. Or to use

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM