Search code examples
jsfjsf-2datatable

Set id of a component within JSF dataTable to value from current item in the array


I'm having a datatable where I would like to set the id of each row to the id of the current item (object that has an id field) in the array that builds the table.

Example:

<h:dataTable
   value="#{bean.list}"
   var="item">
      <h:column>
         <h:outputText id="#{item.id}" .... />
      </h:column>
</h:dataTable>

This doesn't work as I get: javax.servlet.ServletException: Empty id attribute is not allowed.

Is it not possible to set the id this way due to how JSF builds its id's, or am I doing something wrong?


Solution

  • From the JSF UI components, the id and binding attributes are evaluated during view build time, the moment when the XML tree structure in the XHTML/JSP file is to be parsed and converted to a JSF component tree as available by FacesContext#getViewRoot(). However, the <h:dataTable> iterates during view render time, the moment when the JSF component tree needs to produce HTML code by UIViewRoot#encodeAll(). So, at that moment the id attribute is evaluated, the #{item} is nowhere available in the EL scope and evaluates to null which ultimately prints an empty string.

    There are basiclly 3 solutions:

    1. Use a view build time tag like JSTL <c:forEach> so that the #{item} is available during view build time as well.

      <table>
          <c:forEach items="#{bean.list}" var="item">
              <tr><td><h:outputText id="#{item.id}" ... />
      

      See further also JSTL in JSF2 Facelets... makes sense?

    2. Don't print it as ID of a JSF component, but of a plain HTML element.

      <span id="#{item.id}">
      

      Please note that IDs starting with a digit are invalid in HTML as per HTML spec chapter 6.2. You might want to prefix it with some string like so:

      <span id="item_#{item.id}">
      
    3. Don't use a dynamic ID. Just use a fixed ID. JSF will autogenerate an unique ID based on row index anyway.

      <h:outputText id="foo" ... />
      

      This will end up in like <span id="formId:tableId:0:foo"> provided that it's inside a <h:form id="formId"><h:dataTable id="tableId">. The 0 is the 0-based row index which increments every row. This thus guarantees an unique ID in every row without the need to worry about it yourself.