Facelets ui:repeat tag can be used as a substitute of h:dataTable tag from JSF HTML library or c:forEach tag from JSTL core library. While using h:dataTable tag is still the preferred method to create tables, ui:repeat tag gives developer more control over table structure and thus allows to overcome several limitations of h:dataTable tag. As usual this comes with higher level of verbosity but the difference is not very significant. In this post we will convert the project from Data tables in JSF post to use ui:repeat tag. Additionally, we will add row numbers to the table so it will look like this:
General usage
Tag ui:repeat iterates over a collection (a scalar object, an array, java.util.List or java.sql.ResultSet) provided by value attribute and inserts its contents into the final page once for every iteration. Attribute var is used to name and later access the properties of every element of the collection. The most interesting part of ui:repeat tag is an optional varStatus attribute which names an object to be queried for additional data.
Here is an example of a table with 3 columns (row number, name and value):
<table> <ui:repeat value="#{rows} var="row" varStatus="status"> <tr> <td>#{status.index + 1}</td> <td>#{row.name}</td> <td>#{row.value}</td> </tr> </ui:repeat> </table>
Selecting rows
Tag ui:repeat has 3 additional attributes which control which rows should be printed:
- offset – the index from which the iteration should start. The default value is 0.
- step – the step between this and next index. The default value is 1.
- size – the number of iterations. The default value is calculated based on the size of the collection, offset and step: (collectionsize – offset) / step
Status variable
At any point of the iteration we can query object defined by varStatus attribute to get following data:
- begin – the index from which the iteration started (corresponds to offset attribute of ui:repeat tag)
- step – the step between this and the next index (corresponds to step attribute of ui:repeat tag
- end – the index of the next element past the last one (equals offset + step * size)
- index – the index of the current row
- first, last, even, odd – useful logical values for assigning CSS styles to rows
Example
Because we want to show a table with header, footer and caption, we have to create them explicitly:
<?xml version='1.0' encoding='UTF-8' ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title>#{msgs.httpHeaders}</title> <h:outputStylesheet library="default" name="css/styles.css" /> </h:head> <h:body> <table class="table"> <tr class="tableHeader" > <th>#{msgs.id}</th> <th>#{msgs.name}</th> <th>#{msgs.value}</th> </tr> <ui:repeat value="#{headers.entries}" var="entry" varStatus="status"> <tr class="#{status.even ? 'tableRow' : 'tableRowAlt'}"> <td> #{status.index + 1} </td> <td> <h:outputText value="#{entry.name}" /> </td> <td> <h:outputText value="#{entry.value}" /> </td> <f:facet name="caption"></f:facet> </tr> </ui:repeat> <tr class="tableFooter"> <td></td> <td>#{msgs.stopDash}</td> <td>#{msgs.stopDash}</td> </tr> <caption class="tableCaption">#{msgs.httpHeadersCaption}</caption> </table> </h:body> </html>
The single row of the table is represented using following name-value class:
package com.example.jsfrepeat; public class HeaderEntry { private String name; private String value; public HeaderEntry(String name, String value) { this.name = name; this.value = value; } public String getName() { return name; } public String getValue() { return value; } }
The list of all HTTP headers (table rows) is fetched from HTTPServletRequest using getEntries() method:
package com.example.jsfrepeat; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import javax.annotation.PostConstruct; import javax.enterprise.context.RequestScoped; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.inject.Named; import javax.servlet.http.HttpServletRequest; @Named @RequestScoped public class Headers { private List<HeaderEntry> entries; @PostConstruct public void init() { entries = new ArrayList<>(); ExternalContext context = FacesContext.getCurrentInstance().getExternalContext(); HttpServletRequest request = (HttpServletRequest) context.getRequest(); Enumeration<String> namesIt = request.getHeaderNames(); while (namesIt.hasMoreElements()) { String name = namesIt.nextElement(); Enumeration<String> valueIt = request.getHeaders(name); while (valueIt.hasMoreElements()) { String value = valueIt.nextElement(); entries.add(new HeaderEntry(name, value)); } } } public List<HeaderEntry> getEntries() { return entries; } }
Conclusion
Creating tables using ui:repeat tag is more straightforward than using h:dataTable tag and also gives developer more control over final table structure. However, when this additional control is not needed, it is usually much better to use h:dataTable tag which clearly shows developer intent.
The complete source code for the example was tested on JBoss AS 7.1 and is available at GitHub.