6

I have the following basic (and maybe stupid) understanding problem in JSF:

There is one JSF page "testPage.xhtml" :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html">

<f:view>
<h:body>
<h:form>
    <h:commandLink id="B1" value="B1" action="#{testBean.ctrl}"/>

    <h:commandLink id="B2" value="B2" action="#{testBean.ctrl}"
            rendered="#{testBean.renderB2}"/>
</h:form>
</h:body>
</f:view>
</html>

And one backing bean "TestBean.java" :

package test;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean(name="testBean")
@RequestScoped
public class TestBean implements Serializable {

    public static final long serialVersionUID = 1L;

    private boolean renderB2 = false;

    public String ctrl() {
            setRenderB2(true);

            System.out.println("ctrl() is called.");

            return null;
    }

    public boolean getRenderB2() {
            return renderB2;
    }

    public void setRenderB2(boolean renderB2) {
            this.renderB2 = renderB2;
    }
}

So both links have TestBean.ctrl() as action.

First only B1 is rendered. Clicking B1 causes execution of TestBean.ctrl() and B2 is rendered too.

However, clicking B2 then does not execute TestBean.ctrl().

And this is my question: Why is the action method not executed when clicking B2?

Probably it is because B2 is not rendered again.But why does that prevent the execution of the action method (called by the previously rendered B2 link)?

Shawn Chin
  • 84,080
  • 19
  • 162
  • 191
hubba
  • 63
  • 3

1 Answers1

7

That's because the rendered attribute is re-evaluated again during apply request values phase of the form submit. If an UIInput or UICommand component is not rendered at that point, then JSF won't apply the request values for the component. I.e. the UIInput component's model value will not be updated and the UICommand component's action will not be invoked.

Because your bean is request scoped, it's been trashed by end of the response which displays the form and a brand new one is been created by start of the request of the form submit.

You have to preserve any request scoped properties responsible for the rendered attribute. The easiest way is to place the bean in the view scope by marking it @ViewScoped and ensuring that you return null or void from action methods which should postback to the same view.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I suspected that the rendered attribute is the cause for the disappearing action call. – hubba Aug 10 '11 at 15:23
  • That's correct. My answer already elaborated that in detail. Did you understood it? – BalusC Aug 10 '11 at 15:27
  • Thanks for your fast reply.
    I suspected that the rendered attribute is the cause for the action call disappearing somewhere in the lifecycle.
    But is that really logical?
    Is that really wanted by the JSF creators?
    In my thinking the button was rendered when it was clicked and so the action should be executed and the new value for the rendered attribute should only be used in the render response phase?
    (sorry I had some editing problems)
    – hubba Aug 10 '11 at 15:30
  • 1
    You should see it as a safeguard. The enduser (read: hacker) could otherwise easily just manipulate the HTTP request parameters to invoke the button which they are not supposed to invoke by server-side restrictions on the `rendered` attribute. – BalusC Aug 10 '11 at 15:31
  • Okay, I accept, but this use of the render attribute seems to be a little bit puzzling (at least to me ;-) ). I think at least this behaviour should be written out clearly in the API docs - but also possible that only overlooked it. So I'll change it to view scope. Thank you very much again! – hubba Aug 10 '11 at 15:42