A while ago I was searching in Google for the “standard” way to output dynamic tables in JSF. A dynamic table is a table with variable column sizes like in a SQL query:
SELECT * FROM my_table
The tag in JSF for outputting a HTML table is <h:datatable>.
A standard table in JSF looks like that:
<h:datatable value="#{sql.queryContent}" var="row"> <h:column name="header">Col1</h:column>#{row.col1Text} <h:column name="header">Col2</h:column>#{row.col2Text} ... </h:datatable>
As you can see, JSF wants the columns before outputting the content.
So that doesn’t fit right into what I wanted.
There is [h:columns] too, but that doesn’t fit either.
Well, I supposed this should be some very basic question, but I didn’t find an “elegant” solution (doing my google search) with little Java code and a couple of JSF-tags.
What I did find was a solution where an object of the JSF datatable
(javax.faces.component.html.HtmlDataTable) was created and the cells where filled with other JSF internal classes I have never seen before.
http://balusc.blogspot.de/2006/06/using-datatables.html#PopulateDatatable
This solution required a deep insight in the JSF API and was IMHO far from being elegant and easy to read.
So I did some thinking of my own and came to this solution (without using the tag, but using HTML tags):
[sql.xhtml]
<table id="tblSqlResult"> <thead> <tr> <!-- sql.headers is a List of Strings --> <ui:repeat value="#{sql.headers}" var="h"> <th>#{h}</th> </ui:repeat> </tr> </thead> <tbody> <!-- sql.content is a List of List of Strings, e.g. --> <!-- { {row1col1Text, row1col2Text, row1col3Text}, --> <!-- row2col1Text, row2col2Text, row2col3Text} } --> <ui:repeat value="#{sql.content}" var="row"> <tr> <ui:repeat value="#{row}" var="cell"> <td>#{cell}</td> </ui:repeat> </tr> </ui:repeat> </tbody> </table>
Without having to know JSF much, you get the meaning simply by reading the XHTML code. Just like you would write it in standard java with two simple iterations, you do the same in JSF frontend code.
One iteration goes over the rows, the second one over the columns of the row:
<table> ... <ui:repeat value="#{sql.content}" var="row"> <tr> <ui:repeat value="#{row}" var="cell"> <td>#{cell}</td> </ui:repeat> </tr> </ui:repeat> </table>
Ok, wonderful. It works.
But I mentioned something about the elegant and the fast way, didn’t I?
Well, this is the elegant way.
For large tables this is slow as hell.
Why? JSF code gets interpreted by Java, unlike JSP which is compiled into a native Java class. Those two <ui:repeat>s get interpreted over and over again, resulting in really crappy performance for large tables. This is a slam on the brakes. For small tables like paginated tables this is all you need, though. If so, you don’t need to read any further.
For a large datatable I replaced the two tags with “native” java code and got a speed up of 5+ times (in my example).
So I reduced the XHTML code for creating the rows to:
<h:outputText value="#{sql.rowsOfTable}" escape="false"/>
and had to create the rows and the cells of the HTML table in pure java code:
// the rows and cells of the database table List<List<String>> content = new ArrayList<>(); public String getRowsOfTable() { StringBuilder sb = new StringBuilder(); for (List<String> row : content) { sb.append("\t<tr>\n"); for (String cell : row) { sb.append("\t\t<td>" + cell + "</td>\n"); } sb.append("\t</tr>\n"); } return sb.toString(); }
Now the time needed for creating the rows is neglectable (the time comes from reading the database table) and you have a fast solution. At least if you don’t have to rely on the JSF , in this case you have to look for a different approach like the one mentioned above, i.e. creating a JSF datatable (javax.faces.component.html.HtmlDataTable) all by yourself.
This should be an efficient way to output large datatables, too.
So creating the markup in Java code is the fast solution? This is only half of the truth. Outputting a 10 MB table in HTML lets the browser think quite a long time.
But that’s another story.
Thanks for reading.