1

I have a page which takes a GET parameter, for example: /manageSquare.xhtml?squareName=foobar. This page has a backing bean that is request scoped. To properly initialize the backing bean, the bean includes a initialize method which will be called since the page includes a <f:metadata> with an <f:viewParam> and <f:event type="preRenderView ...>. Sometimes the square does not exist, and some special error message will be shown. Therefore there is a <h:panelGroup rendered="#{squareBean.squareExist}"> element.

Now - the problem is that if I have a <h:commandButton> element inside such a <h:panelGroup> then whenever the form is submitted, only navigation takes place but the action method is never called! How can this be?

Here is some conceptual code (boiled down)

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
            xmlns:ui="http://java.sun.com/jsf/facelets"
            xmlns:h="http://java.sun.com/jsf/html"
            xmlns:f="http://java.sun.com/jsf/core"
            xmlns:fn="http://java.sun.com/jsp/jstl/functions"
            template="../template.xhtml">
<ui:param name="title" value="Square"/>
<f:metadata>
    <f:viewParam name="squareName" value="#{manageSquareBean.squareName}"/>
    <f:event type="preRenderView" listener="#{manageSquareBean.initialize}"/>
</f:metadata>
<ui:define name="content">
    <h1>Manage square <h:outputText escape="true" value="#{manageSquareBean.squareName}"/></h1>
    <h:panelGroup rendered="#{!manageSquareBean.squareExists}">
        <span class="error">Square does not exist</span>
    </h:panelGroup>
    <h:panelGroup rendered="#{manageSquareBean.squareExists}">
        <h:form>           
            <h:commandButton action="#{manageSquareBean.addUsersToSquare}" value="Add users">
                <f:param name="squareName" value="#{manageSquareBean.squareName}"/>
            </h:commandButton>
         </h:form>
    </h:panelGroup>
</ui:define>
</ui:composition>

and here is the a managedBean:

@RequestScoped
@ManagedBean
public class ManageSquareBean
{
    private String squareName;

    private boolean squareExists;
    private List<String> usersToAdd;

    public void initialize()
    {
        // Find out if squareName exists
        squareExists = squareExists(squareName);
    }

    public String addUsersToSquare()
    {
        // Do something
    }

    // Getters and setters
}

I really do not understand why this is happening. Removing the <panelGroup> and everything is fine. Also, it seems the squareName param is not sent forward when the commandButton is pressed.

Here is a person having the same problem. He solved it using JSTL instead of <panelGroup>, but JSTL will run on build time which is before the initialize method is called.

Please help, I have used many hours on this problem.

foens
  • 8,642
  • 2
  • 36
  • 48

2 Answers2

2

The rendered attribute of the input and command component and all its parents is re-evaluated during the apply request values phase of the form submit. Since your bean is request scoped, a completely new bean is created upon the submit and therefore the condition of the rendered attribute defaults to false and hence the button is never rendered and thus also never invoked.

Your bean needs to be placed in the view scope.

@ManagedBean
@ViewScoped
public class ManageSquareBean

This way the bean (and all boolean conditions it holds) will live as long as you're interacting with the same view.

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Hmm. That actually works. I do not understand why. According to your explanation, everything is reevaluated. I have checked using the debugger that the squareExists is set to true, both when rendering the form and when posting it. Therefore I do not really understand why this works. Are you saying that my initialize function is not called in the RequestScoped bean when the request values are applied? – foens Sep 16 '11 at 16:11
  • The initialize function is called during invoke action phase. Until that point, The boolean is still false because it's the default value for a brand new bean instance. The apply request values phase (when JSF rechecks that condition in order to gather submitted values and pressed buttons/links) runs **before** invoke action phase. – BalusC Sep 16 '11 at 16:12
0

I'm sure that the panelGroup isn't the problem.

I have 2 suggestions for you:

1) Check if you have inherited forms. Sometimes it happens that a form element resides in another form through the templating approach. JSF can't handle this correctly

2) Perhaps you need to wrap the f:metadata with a define tag and the name metadata.

http://javaserverfaces.java.net/nonav/docs/2.0/pdldocs/facelets/f/metadata.html

<ui:define name="metadata">
<f:metadata>
    <f:viewParam name="squareName" value="#{manageSquareBean.squareName}"/>
    <f:event type="preRenderView" listener="#{manageSquareBean.initialize}"/>
</f:metadata>
</ui:define>
user948392
  • 176
  • 8
  • 1) There is no nested forms. However, the viewstate id is defined multiple times - might this be a problem? See [pastebin](http://pastebin.com/JXJfbbUc) for full html. 2) Tried it, but didn't change anything. The listener is called either way. – foens Sep 16 '11 at 08:55
  • Also, how come that if I remove the PanelGroup in the example, it just works? I really think it is the problem - but I have no idea if I am correct or it is something else as you are hinting about. – foens Sep 16 '11 at 09:01
  • The ViewState is not the problem. I just looked at our project and found the same behavior. – user948392 Sep 16 '11 at 10:27
  • Can you try to move the form out of the panelGroup. It's just a guess, but perhaps there is a problem with the rendered and the form. Normally this should work, but who knows. By the way, which jsf version do you have? – user948392 Sep 16 '11 at 10:29
  • Good points, but really not applicable on OP's particular problem. Nested forms can't be the cause because it works when the panelgroup is removed. The metadata declaration is perfectly fine. It just needs to be a child of f:view. – BalusC Sep 16 '11 at 14:33