c:forEach inside primefaces(e.g. p:panelgrid) inside ui:repeat

Antonio Ragagnin picture Antonio Ragagnin · Feb 11, 2014 · Viewed 22.5k times · Source

I have to dinamically produce a list of tables. Each of these have a variable number of columns (with fixed rows).

To perform this I first put a <p:panelGrid> inside an <ui:repeat>: so I correctly produced a list of tables.

Then, to dinamically produce the columns, I tried put both an <ui:repeat> or a <c:forEach> inside the <p:panelGrid>. In the result I obtain no rows.

I written a minimal example here. In the bean testBackingBean, I have defined (and initialized) the variable ArrayList<ArrayList<String>> tables. This is the xhtml that does not produce the expected results:

<ui:repeat var="table" value="#{testBackingBean.tables}">
    <p:panelGrid>
        <f:facet name="header">
            <p:row>
                <p:column >header of #{table}</p:column>
            </p:row>
        </f:facet>
        <p:row>
            <c:forEach var="row" items="${table}">
                <p:column>#{row}</p:column>
            </c:forEach>
        </p:row>
    </p:panelGrid>
</ui:repeat>

Noteworthy the header-row correctly converts #{table} into string. The problem is that I see no rows of data.

Also, if I use the <table> instead of the <p:panelGrid> everything work as excpected.

Also, I tried different permutations of <c:forEach> and <ui:repeat> with no succes.

So, how can I dinamically produce more tables (using prime-faces) and set a dinamical number of columns?

Thanks!

EDIT: I would like to use two <c:forEach>, but even with one only <c:forEach> I get an empty result. In fact if I try the following xhtml:

<c:forEach  items="${testBackingBean.tables}" var="tabella">
    current element: #{tabella}
</c:forEach>

then I get an empty result. (I know,this is a different question)

Answer

BalusC picture BalusC · Feb 11, 2014

The transition from the XHTML source code to the generated HTML output is a two-step process.

  1. First, during view build time, the XHTML source code is parsed and turned in a tree of Java UIComponent instances representing the JSF UI component tree, as available by FacesContext#getViewRoot().

  2. Then, during view render time, the JSF UI component tree produces HTML output and writes it to the HTTP resopnse, starting with UIViewRoot#encodeAll() method.

Taghandlers like all JSTL <c:xxx> tags, several JSF <f:xxx> tags and only a few Facelets <ui:xxx> tags run during view build time. UI components like all JSF <h:xxx> tags, several Facelets <ui:xxx> tags and only a few JSF <f:xxx> tags run during view render time.

The <c:forEach> is a taghandler and the <ui:repeat> is an UI component.

In other words, the UI components which are declared inside <c:forEach> are recreated multiple times in the JSF component tree based on <c:forEach items> during view build time which in turn individually produce each their own HTML output during view render time. The UI components which are declared inside <ui:repeat> are created only once in the JSF component tree during view build time which in turn are reused multiple times based on <ui:repeat value> to produce HTML output during view render time.

Your concrete problem is caused by the fact that <ui:repeat var="table"> is only available during view render time, not during view build time. The <c:forEach> is basically retrieving a #{null} as value when it's about to run during view build time.

You can solve this by replacing the outer <ui:repeat> by <c:forEach>. Although I wonder if you couldn't better use <ui:repeat><p:dataTable><p:columns> instead.

See also: