Initialize a composite component based on the provided attributes

composite-component, jsf, jsf-2

Solution

As to the cause, `UIComponent` instances are inherently request scoped. The postback effectively creates a brand new instance with properties like `values` reinitialized to default. In your implementation, it is only filled during `encodeXxx()`, which is invoked long after `decode()` wherein the action event needs to be queued and thus too late.

You'd better fill it during the initialization of the component. If you want a `@PostConstruct`-like hook for `UIComponent` instances, then the `postAddToView` event is a good candidate. This is invoked directly after the component instance is added to the component tree.

<cc:implementation>
    <f:event type="postAddToView" listener="#{cc.init}" />
    ...
</cc:implementation>

with

private List<String> values;

public void init() {
    values = new ArrayList<String>();
    Integer num = (Integer) getAttributes().get("value");

    for (int i = 0; i < num; i++) {
        values.add("item" + i);
    }
}

(and remove the `encodeBegin()` method if it isn't doing anything useful anymore)

An alternative would be lazy initialization in `getValues()` method.

Problem

I'm writing my custom table composite component with Mojarra JSF. I'm also trying to bind that composite to a backing component. The aim is to be able to specify the number of elements the table has in a composite attribute, later on the bound backing component will autogenerate the elements itself before view gets rendered. I've this sample code: Main page: ``` <html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:comp="http://java.sun.com/jsf/composite/comp"> <h:head /> <body> <h:form> <comp:myTable itemNumber="2" /> </h:form> </body> </html> ``` myTable.xhtml: ``` <html xmlns="http://www.w3.org/1999/xhtml" xmlns:composite="http://java.sun.com/jsf/composite" xmlns:h="http://java.sun.com/jsf/html"> <h:body> <composite:interface componentType="components.myTable"> <composite:attribute name="itemNumber" type="java.lang.Integer" required="true" /> </composite:interface> <composite:implementation> <h:dataTable value="#{cc.values}" var="value"> <h:column headerText="column"> #{value} <h:commandButton value="Action" action="#{cc.action}" /> </h:column> </h:dataTable> </composite:implementation> </h:body> </html> ``` MyTable.java: ``` @FacesComponent("components.myTable") public class MyTable extends UINamingContainer { private List<String> values = new ArrayList<String>(); public void action() { System.out.println("Called"); } @Override public void encodeBegin(FacesContext context) throws IOException { // Initialize the list according to the element number Integer num = (Integer) getAttributes().get("itemNumber"); for (int i = 0; i < num; i++) { values.add("item" + i); } super.encodeBegin(context); } public List<String> getValues() { return values; } } ``` The issue is table gets rendered properly (in this case with two items), but `action` method doesn't get called when pressing the button on the lines. If I follow the wiki page for composite components, I can get it work in that way, but having to initialize the `List` each time `getValues()` is called, introducing logic into the getter method :-(. Any idea about that? It seems to be a trouble related with overriding `encodeBegin` method. I also tried initializing it on `markInitialState`, but attributes are not yet available there... Tested with Mojarra 2.1.27 + Tomcat 6-7 & Mojarra 2.2.5 + Tomcat 7

Original source