7

Using JSF 1.2 and JSP....

Is it possible to iterate over a Map whose values contain Collections?

I have a Map that looks like this:

Map<String, List<Foo>> myMap;

I would like to iterate over myMap and draw a separate table for each key.

Each table will contaim multiple rows.

Each row will represent a Foo object from the ArrayList mapped to the current key.

Sadly we are using JSF 1.2 and JSP.

I was hoping I could use a nested <h:dataTable> tag, but I'm not having any success.


Edit:

Here is my current JSP code after consulting BalusC's answer:

                    <c:forEach items="#{someModule$someBean.prefMap}" var="mapEntry">
                        <br/><br/><p>Key: <h:outputText value="#{mapEntry.key}"/></p>
                        <h:dataTable value="#{mapEntry.value}" var="pref">
                            <h:column><h:outputText value="#{pref.defaultFieldLabel}"/></h:column>
                            <h:column><h:outputText value="#{pref.fieldLabel}"/></h:column>
                        </h:dataTable>
                    </c:forEach>

It causes the following exception:

javax.servlet.jsp.JspTagException: Don't know how to iterate over supplied "items" in <forEach>


Here is some code from my Managed Bean.

Note that I'm using HashMap and ArrayList instead of Map and List

(I read somewhere you couldn't use interfaces, which also don't work)

private HashMap<String, ArrayList<Foo>> prefMap;

public HashMap<String, ArrayList<Foo>> getPrefMap()
{
  if (prefMap == null)
  {
    buildPrefMap();
  }
  return prefMap;
}

private void buildPrefMap()
{
  prefMap = new HashMap<String, ArrayList<Foo>>();
  for (Foo mdp : getFooArray())
  {
    String cat = mdp.getField().getCategory();
    if (! prefMap.containsKey(cat))
    {
      ArrayList<Foo> mpl = new ArrayList<Foo>();
      mpl.add(mdp);
      prefMap.put(cat, mpl);
    }
    else
    {
      prefMap.get(cat).add(mdp);
    }
  }
}

private void dumpMapInfo()
{
  StringBuilder sb = new StringBuilder();
  Map<String, ArrayList<Foo>> theMap = getPrefMap();
  for (String key : theMap.keySet())
  {
    sb.append(key + ": " + theMap.get(key).size());
  }
  System.out.println("\n\n" + sb.toString());
}

Calling dumpMapInfo before rendering the page confirms that the Map is not null and is populated as expected.

jahroy
  • 22,322
  • 9
  • 59
  • 108

1 Answers1

17

JSF doesn't have any component which can iterate over a Map. Only the JSTL <c:forEach> can iterate over a Map. Each iteration gives you a Map.Entry back which in turn has getKey() and getValue() methods. You can in turn use a <h:dataTable> to iterate over the map value.

<c:forEach items="#{bean.map}" var="entry">
    <p>Key: <h:outputText value="#{entry.key}" /></p>
    <h:dataTable value="#{entry.value}" var="foo">
        <h:column><h:outputText value="#{foo.prop1}" /></h:column>
        <h:column><h:outputText value="#{foo.prop2}" /></h:column>
        <h:column><h:outputText value="#{foo.prop3}" /></h:column>
    </h:dataTable>
</c:forEach>

Update this works only when you're using JSTL 1.2. This fails in JSTL 1.1 because #{} is not recognized in JSTL 1.1 tags and hence you're forced to use ${}, but this in turn fails in the nested JSF components because they accept #{} only. You'd basically need to fall back to "plain" JSP/HTML in that entire piece of code, or, better, grab Tomahawk's <t:dataList>.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I get the following exception: `javax.servlet.jsp.JspTagException: Don't know how to iterate over supplied "items" in <forEach>` Note that we are using JSF 1.2 and are using Sun Application Server 9.0_1 – jahroy Oct 03 '12 at 20:56
  • Works fine for me with [JSTL 1.2](http://stackoverflow.com/tags/jstl/info) and [Tomcat 6.0.35](http://tomcat.apache.org/download-60.cgi) (with `web.xml` set to Servlet 2.5). What JSTL and container version exactly are you using? – BalusC Oct 03 '12 at 21:01
  • SJAS 9.0 uses JSTL 1.1, but it should work as good. Are you certain that the EL syntax is correct? You could get this error on e.g. `` without the `#{}` or `${}`. – BalusC Oct 03 '12 at 21:03
  • How do I check which JSTL version we're using? I believe we are using Servlet 2.5 (web.xml says versoin="2.5"). – jahroy Oct 03 '12 at 21:14
  • 1
    `System.out.println(ForEachTag.class.getPackage().getImplementationVersion());`. – BalusC Oct 03 '12 at 21:16
  • Ok... I don't mind using a helper method to iterate over Map.keySet() as long as I can achieve nested looping to iterate over the map keys **AND** the array lists in the buckets. – jahroy Oct 03 '12 at 21:20
  • I take my words back. It actually works on JSTL 1.1 when you remove the `$` from the backing bean name and use `${}` notation "the usual JSP way". It just won't mix with `$` in bean name and with `#{}` in nested JSF components. – BalusC Oct 03 '12 at 21:22
  • Unfortunately those idiotic bean names are generated by the framework we use and they cannot be modified. Also, we need to use nested JSFF components. So... In other words it sounds like we may be SOL. – jahroy Oct 03 '12 at 21:24
  • Try upgrading to JSTL 1.2. I'm however afraid that you've to modify the server libs for this. I think it's after all better to adopt Tomahawk for its `` (and the `` awesomeness which simulates JSF2 view scope). – BalusC Oct 03 '12 at 21:26
  • I've added Tomahawk to our project. Looks like I will have to create a helper class to provide _map iteration functionality_ since `` does not operate on a Map. – jahroy Oct 03 '12 at 23:36