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