tags:

views:

758

answers:

2

I am having a problem with custom components in facelets. The first time that the page is rendered, the attributes are set properly on the component class. When a form is submitted however, the attributes are not set.

Here is the class that I am using to test this.

public class TestEcho extends UIData
{
    /** Logger. */
    private static Log log = LogFactory.getLog(TestEcho.class);

    private String msg;

    public TestEcho()
    {
        log.debug("Constructor.");
    }

    public void encodeEnd(FacesContext context) throws IOException
    {
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement("div", this);
        writer.writeText("The value of msg is '" + msg + "'.", null);
        writer.endElement("div");
    }

    public void setMsg(String msg)
    {
        log.debug("Setting msg to '" + msg + "'.");
        this.msg = msg;
    }
}

The component is used in the .xhtml page like this.

<h:form>
    <v:testEcho msg="hello" />
    <h:commandButton action="#{PictureManager.trigger}" value="Click" />
</h:form>

When the page renders for the first time, the component renders the following html code.

<div>The value of msg is 'hello'.</div>

When the button is clicked, it renders this.

<div>The value of msg is 'null'.</div>

From the log, you can see that the component is constructed again, but the attribute is not set.

13:23:42,955 DEBUG [TestEcho] Constructor.
13:23:42,955 DEBUG [TestEcho] Setting msg to 'hello'.
----- Button was pressed here -----
13:25:48,988 DEBUG [TestEcho] Constructor.
13:25:49,144 DEBUG [PictureManager] Button pressed.

From what I understand, facelets does all the wiring of attributes to components so I don't need a tag class, but I don't understand why the attribute would be set correctly the first time, but not the second time.

+1  A: 

You must save your state by overriding the saveState and restoreState methods.

So, saveState must return a Serializable object (e.g. a JavaBean or Object[] array) containing the value in msg and whatever is returned by super.saveState. This object will be provided to restoreState where the method must restore msg from the object and pass the parent state to super.restoreState.

McDowell
A: 

McDowell's answer did it. Just for completeness, here's the two methods I added.

public Object saveState(FacesContext context)
{
    Object[] rtrn = new Object[2];
    rtrn[0] = super.saveState(context);
    rtrn[1] = msg;
    return rtrn;
}

public void restoreState(FacesContext context, Object state)
{
    Object[] values = (Object[]) state;
    super.restoreState(context, values[0]);
    msg = (String) values[1];
}
Matt McMinn