4

I have two variables "userId" and "name". When I click for example the "SHOW USERID" button it works fine and sets "renderUserId=true" and it shows it with the "render", but then if I click the other "SHOW" button, the Bean is reconstruct and I loose the "renderUserId=true" and it becomes "false" and "renderName=true" so it shows ok, but the USERID is hidden.

My question is, how can I avoid loosing the bean values when I render the xhtml?

This is a simple simulation of my code.

NOTE: if I use "actionListener" instead of "f:setPropertyActionListener" in the "h:commandButton" I have the same result, the bean is reconstruct

example.xhtml

<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:a4j="http://richfaces.org/a4j"
    xmlns:cc="http://java.sun.com/jsf/composite">

    <cc:interface componentType="com.bean.ExampleBean">
        <cc:attribute name="userId" type="java.lang.Integer"/>
        <cc:attribute name="name" type="java.lang.String"/>
    </cc:interface>

    <cc:implementation>

        <h:panelGrid id="example_panel" columns="1" width="100%">

            <h:outputText value="USERID: #{cc.attrs.userId}" rendered="#{!cc.attrs.renderUserId}"/>

            <a4j:commandButton value="SHOW USERID" render="example_panel"
                rendered="#{!cc.attrs.renderUserId}">
                <f:setPropertyActionListener value="#{true}"
                    target="#{cc.attrs.renderUserId}"/>
            </a4j:commandButton>                
            <a4j:commandButton value="HIDE USERID" render="example_panel"
                rendered="#{cc.attrs.renderUserId}">
                <f:setPropertyActionListener value="#{false}"
                    target="#{cc.attrs.renderUserId}"/>
            </a4j:commandButton>                


            <h:outputText value="NAME: #{cc.attrs.name}" rendered="#{!cc.attrs.renderName}"/>

            <a4j:commandButton value="SHOW NAME" render="example_panel"
                rendered="#{!cc.attrs.renderName}">
                <f:setPropertyActionListener value="#{false}"
                    target="#{cc.attrs.renderName}"/>
            </a4j:commandButton>                
            <a4j:commandButton value="HIDE NAME" render="example_panel"
                rendered="#{cc.attrs.renderName}">
                <f:setPropertyActionListener value="#{false}"
                    target="#{cc.attrs.renderName}"/>
            </a4j:commandButton>                


        </h:panelGrid>

    </cc:implementation>

</ui:composition>

ExampleBean.java

import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;


@FacesComponent("com.bean.ExampleBean")
public class ExampleBean extends UINamingContainer {

    private Integer userId;
    private String name;

    private boolean renderUserId;
    private boolean renderName;

}
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
davidml
  • 153
  • 5
  • 20
  • That's not a custom component. That's a composite component. I fixed the title and tags accordingly. Please pay attention to terminology. See also http://stackoverflow.com/questions/6822000/when-to-use-uiinclude-tag-files-composite-components-and-or-custom-componen/6822269#6822269 – BalusC Apr 10 '13 at 13:10

2 Answers2

12

There's a major misconception going here. That's not a backing bean. That's a backing component.

JSF UI component instances are not view scoped, instead they are request scoped. They are destroyed by end of render response (after having saved their state into JSF view state) and recreated during view build time (and their state is restored from JSF view state).

You've assigned the stateful properties as instance variables of the component. This is not right. You should be explicitly storing them in the JSF state. The correct approach for that is to let the getter and setter delegate to UIComponent#getStateHelper(). Any attributes which are declared as <cc:attribute> already implicitly do that. You do absolutely not need to redeclare them as instance variables of the backing component.

Those booleans which are not declared as <cc:attribute> must be reimplemented like follows:

public Boolean getRenderUserId() {
    return (Boolean) getStateHelper().eval("renderUserId", Boolean.FALSE);
}

public void setRenderUserId(Boolean renderUserId) {
    getStateHelper().put("renderUserId", renderUserId);
}

In your action(listener) method, just invoke setRenderUserId(true) accordingly.

Don't forget to fix the EL expressions accordingly:

#{cc.renderUserId} 

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • That works fine! But it's still not the solution I was looking for. I'm searching kind of component where I can have the attributes like view scope with the possibility of change theme and submit all info when client wants. If this solution is request scope is not what I'm looking for. Still thx a lot! – davidml Apr 10 '13 at 15:14
  • Then just create a normal backing bean class for the data instead of component class. – BalusC Apr 10 '13 at 15:15
  • Just one quick comment a little off topic, but why use Boolean.false instead just false? – rekiem87 Oct 17 '14 at 21:36
1

Your FacesComponent does not keep a state, it's a new instance everytime you call it. For your use case it seems you should use a ManagedBean with a view scope at least. This means that as long as you're on the page with the buttons, the instance is kept.

Nina
  • 320
  • 2
  • 10
  • Or, if on `Mojarra` you could consider upgrading to `2.2` so you can use the view scope while sticking to `CDI`. – Menno Apr 10 '13 at 11:13