tags:

views:

2121

answers:

3

I have a Hashtable<Integer, Sport> called sportMap and a list of sportIds (List<Integer> sportIds) from my backing bean. The Sport object has a List<String> equipmentList. Can I do the following using the unified EL to get the list of equipment for each sport?

<h:dataTable value="#{bean.sportIds}" var="_sportId" > 
  <c:forEach items="#{bean.sportMap[_sportId].equipmentList}" var="_eqp">
    <h:outputText value="#{_eqp}"></h:outputText>
    <br/>
  </c:forEach>
</h:dataTable>

I get the following exception when trying to run this JSP code.

 15:57:59,438 ERROR [ExceptionFilter] exception root cause
javax.servlet.ServletException: javax.servlet.jsp.JspTagException: 
      Don't know how to iterate over supplied "items" in &lt;forEach&gt;
Here's a print out of my environment
Server: JBossWeb/2.0.1.GA
Servlet Specification: 2.5
JSP version: 2.1
JSTL version: 1.2
Java Version: 1.5.0_14

Note: The following does work using a JSF tag. It prints out the list of equipment for each sport specified in the list of sportIds.

<h:dataTable value="#{bean.sportIds}" var="_sportId" > 
    <h:outputText value="#{bean.sportMap[_sportId].equipmentList}">
    </h:outputText>
</h:dataTable>

I would like to use the c:forEach tag. Does anyone know if this is possible? If not, anyone have suggestions? In the end I want a stacked list instead of the comma seperated list provided by equipmentList.toString(); (Also, don't want to override toString()).

A: 

Two issues:

  1. A dataTable can only have the following children: header facet, footer facet, column. Anything else is not going to be evaluated correctly.
  2. JSTL tags cannot be interweaved with JSF components. The JSTL tags are evaluated when the component tree is created. JSF components are evaluated when the page is rendered. Thus, the c:forEach tag is only evaluated once - when the component tree is created, which is likely before "#{bean.sportIds}" is available.

Either use a JSF component library that provides the looping like you desire, build one that does the looping you desire, or refactor the beans so that instead of looping over the sportIds, loop over a list of sports where each sport has its id and equipment.

+1  A: 

@keith30xi.myopenid.com

Not TRUE in JSF 1.2. According to the java.net wiki faq they should work together as expected.

Here's an extract from each faq:

JSF 1.1 FAQ
Q. Do JavaServer Faces tags interoperate with JSTL core tags, forEach, if, choose and when?

A. The forEach tag does not work with JavaServer Faces technology, version 1.0 and 1.1 tags due to an incompatibility between the strategies used by JSTL and and JavaServer Faces technology. Instead, you could use a renderer, such as the Table renderer used by the dataTable tag, that performs its own iteration. The if, choose and when tags work, but the JavaServer Faces tags nested within these tags must have explicit identifiers.

This shortcoming has been fixed in JSF 1.2.

JSF 1.2 FAQ
Q. Do JavaServer Faces tags interoperate with JSTL core tags, forEach, if, choose and when?

A. Yes. A new feature of JSP 2.1, called JSP Id Consumer allows these tags to work as expected.

Has anyone used JSF tags with JSTL core tags specifically forEach?

Joe Dean
+1  A: 

I had the same problem once, and I couldn't find a solution using dataTable. The problem is that the var _sportId can be read only by the dataTable component.

If you need to do a loop inside a loop, you can use a dataTable inside a dataTable:

<h:dataTable value="#{bean.sportIds}" var="_sportId" > 
  <h:dataTable value="#{bean.sportMap[_sportId].equipmentList}" var="_eqp">
    <h:outputText value="#{_eqp}"></h:outputText>
  </h:dataTable>
</h:dataTable>

But in this case each of yours equipmentList items is printed inside a table row. It was not a great solution form me.

I chose to use a normal html table instead of a dataTable:

<table>
    <c:forEach items="#{bean.sportIds}" var="_sportId">
      <tr>
        <td>
        <c:forEach items="#{bean.sportMap[_sportId].equipmentList" var="_eqp">
         <h:outputText value="#{_eqp} " />
        </c:forEach>
        </td>
      </tr>
    </c:forEach>
</table>

It works. If you need some specific dataTable functionality like binding and row mapping, you can obtain it in an easy way using the f:setPropertyActionListener tag.

alexmeia