Search code examples
cssjsfjsf-2

h:dataTable always displays one row; won't display zero rows


I'm working in a legacy JSF web app, and my h:dataTable element is giving me trouble. Normally, it displays exactly how I want it - a header and several rows, all with proper padding and margins and everything.

h:datatable displaying three rows correctly

However, if I try to display a table with zero rows (which is a valid use case for me), JSF still renders one row, albeit empty of contents.

h:dataTable rendering one empty row when it should be rendering zero rows

Here's the source code for this h:dataTable:

<h:dataTable styleClass="table" value="#{backingBean.emptyList}" var="result">

    <h:column>
        <f:facet name="header">First Column</f:facet>
        <h:outputText value="#{result}"/>
    </h:column>

    <h:column>
        <f:facet name="header">Second Column</f:facet>
        <h:outputText value="#{result}"/>
    </h:column>

    <h:column>
        <f:facet name="header">Third Column</f:facet>
        <h:outputText value="#{result}"/>
    </h:column>

</h:dataTable>

Here's what's being rendered by the browser:

<table class="table">
    <thead>
        <tr>
            <th scope="col">First Column</th>
            <th scope="col">Second Column</th>
            <th scope="col">Third Column</th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </tbody>
</table>

Here are the methods in the backing bean that give me my result list:

public List<String> getEmptyList() { // incorrectly renders 1 empty row
    return Collections.emptyList();
}

public List<String> getThreeRows() { // correctly renders 3 rows
    return Arrays.asList(new String[] {"row1", "row2", "row3"});
}

Why is JSF rendering this empty row? I would have expected the <tbody> to just be empty. Is this the correct behavior for JSF? or do I have something misconfigured?

Please advise,

-August


Solution

  • As per the source code of Mojarra 2.3.8 this is encouraged behavior, the TableRenderer (as its' name says) is responsible for and explicitly doing this:

    com.sun.faces.renderkit.html_basic.TableRenderer.encodeChildren(FacesContext, UIComponent):

    if(!renderedRow) {
            // if no row with data has been rendered, render that empty row in question:
            this.renderEmptyTableRow(writer, data);
    }
    

    Your options include:

    1) Add a rendered attribute to your dataTable:

    <h:dataTable value="#{backingBean.entityList}"
      rendered="#{not empty backingBean.entityList}" ...>
      ...
    </h:dataTable>
    <h:outputText rendered="#{empty backingBean.entityList}"
      value="No data to display, sooo sorry :-("/>
    

    2) Override the TableRenderer to do the job better fitting your needs:

    package my;
    
    import java.io.IOException;
    
    import javax.faces.component.UIComponent;
    import javax.faces.component.UIData;
    import javax.faces.context.FacesContext;
    
    import com.sun.faces.renderkit.html_basic.TableRenderer;
    
    public class CustomTableRenderer extends TableRenderer {
        @Override
        public void encodeChildren(final FacesContext context, final UIComponent component) throws IOException {
            final UIData data = (UIData) component;
            final int rowCount = data.getRowCount();
            if (rowCount > 0) {
                super.encodeChildren(context, component);
            } else {
                // do what super.encodeChildren does, but your way ...
            }
        }
    }
    

    Sadly you can not just override com.sun.faces.renderkit.html_basic.TableRenderer.renderEmptyTableRow(ResponseWriter, UIComponent) and make it do nothing because it is private.

    In faces-config.xml register your custom renderer:

    <render-kit>
        <renderer>
            <component-family>javax.faces.Data</component-family>
            <renderer-type>javax.faces.Table</renderer-type>
            <renderer-class>my.CustomTableRenderer</renderer-class>
        </renderer>
    </render-kit>
    

    Edit: The behavior in question was introduced in a commit fixing an issue #1009 with comment:

    Fix for issue 1009: Rendered h:dataTable/h:panelGrid without rows/content do not validate against XHTML 1.0 Transitional (and html4)
    
    git-svn-id: https://svn.java.net/svn/mojarra~svn/trunk@7669 761efcf2-d34d-6b61-9145-99dcacc3edf1
    

    Too bad I could not find that issue anymore, but @Kukeltje did!