Dynamic columns with richfaces 4

Karl Kildén picture Karl Kildén · Dec 12, 2012 · Viewed 7k times · Source

I need dynamic number of columns. Richfaces supplies it with <rich:columns> in richfaces 3.3.3-final but for Richfaces 4 they seem to recommend <c:forEach>.

c:forEach

I can't get it to work properly.Since I can't depend on the var from the datatable I can't figure out how to feed <c:forEach> with the correct list of columns. (Each row has their own values but headers are the same)

Basically the data I want to display is a list with rows of x size, each row has a list of column values with y size. But how can have <c:forEach> tell the backing bean what row it's at so I can feed the correct columns?

ui/a4j:repeat

I dont want to reinvent the wheel because I need frozen columns and many other features. Creating the table html this way and use jQuery for other features have been considered. However this would be hopeless to maintain and to much work.

I also looked at constructing it from the backing bean creating children dynamically but I don't like that at all. This would have to be the last resort.

Using: Tomcat 7, servlet 3.0, JSF 2.1x - Mojarra, Richfaces 4.x

Update

Okay so I'm getting some results finally. However my headers don't show. The values show perfectly but not the headers. Some problem doing them with iteration or something perhaps?

    <rich:dataTable value="#{controller.rows}"
        var="row">
        <c:forEach items="#{controller.columns}" var="column">
            <rd:column id="name" width="250">
                <f:facet name="header">
                    <h:outputText value="#{row.myArrayList[column].header}" />
                </f:facet>
                <h:inputText value="#{row.myArrayList[column].value}"  disabled="#{row.myArrayList[column].open}"/>
            </rd:column>
        </c:forEach>
    </rich:dataTable>

Answer

BalusC picture BalusC · Dec 12, 2012

The <c:forEach> is indeed best what you can get. The <ui/a4j:repeat> won't work as that runs during view render time while the UIData component really needs UIColumn children, not UIRepeat children.

In order to get the <c:forEach> to work, you need to supply it a list/map of all property names (and in case of a map maybe also header labels). Here's a concrete kickoff example assuming that Item has properties id, name and value and that #{bean.itemPropertyNames} returns a List<String> with exactly those property names.

<rich:dataTable value="#{bean.items}" var="item">
    <c:forEach items="#{bean.itemPropertyNames}" var="itemPropertyName">
        <rich:column>
            #{item[itemPropertyName]}
        </rich:column> 
    </c:forEach>
</rich:dataTable>

If you need to show column headers as well, then best is to have a Map<String, String> where the key represents the property name and the value represents the header value.

<rich:dataTable value="#{bean.items}" var="item">
    <c:forEach items="#{bean.itemProperties}" var="itemProperty">
        <rich:column>
            <f:facet name="header">#{itemProperty.value}</f:facet>
            #{item[itemProperty.key]}
        </rich:column> 
    </c:forEach>
</rich:dataTable>

Either way, the only disadvantage is that the #{bean} of <c:forEach items> can in this construct not be a view scoped one. It would be recreated on every request, unless you turn off partial state saving. It needs to be a request scoped one (or session or application). Note that it does not necessarily need to be the same bean as the one in <rich:dataTable value>.

See also: