Using java.util.Map in h:dataTable
Asked Answered
G

3

18

I need to display Map using <h:dataTable>. My backing bean has a Map property as below:

public class Bean {

    private Map<Integer,String> map; // +getter

    @PostConstruct
    public void init() {
        map = new TreeMap<Integer,String>();
        map.put(1,"Sasi");
        map.put(2,"Pushparaju");
        map.put(3,"Venkat Raman");
        map.put(3,"Prabhakaran");
    }

}

Then in JSF page I am trying to bind this Map property to the value attribute of <h:dataTable>.

 <h:dataTable border="1" value="#{bean.map}" var="map">
    <h:column id="column1">
        <f:facet name="header">
            <h:outputText value="UserId"></h:outputText>
        </f:facet>
        <h:outputText value="#{map.getKey}"></h:outputText>
    </h:column>
    <h:column id="column2">
        <f:facet name="header">
            <h:outputText value="Email Id"></h:outputText>
        </f:facet>
        <h:outputText value="#{map.getValue}"></h:outputText>
    </h:column>
</h:dataTable>

It is giving en error that getKey and getValue is not present. I can understand that this is not the correct way to do it. How can I present a Map using <h:dataTable>?

Glamour answered 15/1, 2013 at 17:54 Comment(0)
I
31

Until upcoming JSF 2.3, UIData components such as <h:dataTable>, <p:dataTable>, etc and <ui:repeat> does not support iterating over a Map. This is only supported in <c:forEach>.

One way is to convert the map entries to an array (alone entrySet() won't work as UIData also doesn't support Set until upcoming JSF 2.3).

<h:dataTable value="#{bean.map.entrySet().toArray()}" var="entry">
    <h:column>#{entry.key}</h:column>
    <h:column>#{entry.value}</h:column>
</h:dataTable>

Another way is to wrap the map's entry set in a collection which the <h:dataTable> can iterate over, such as an ArrayList.

public class Bean {

    private Map<Integer, String> map;
    private List<Entry<Integer, String>> entries; // +getter (no setter necessary)

    @PostConstruct
    public void init() {
        map = new TreeMap<>();
        map.put(1, "Sasi");
        map.put(2, "Pushparaju");
        map.put(3, "Venkat Raman");
        map.put(4, "Prabhakaran");
        entries = new ArrayList<>(map.entrySet());
    }

    // ...
}
<h:dataTable value="#{bean.entries}" var="entry">
    <h:column>#{entry.key}</h:column>
    <h:column>#{entry.value}</h:column>
</h:dataTable>

However, more clean, self documenting and reusable is to use a List<User> instead wherein the User class has the necessary properties id and name.

public class Bean {

    private List<User> users; // +getter (no setter necessary)

    @PostConstruct
    public void init() {
        users = new ArrayList<>();
        users.add(new User(1, "Sasi"));
        users.add(new User(2, "Pushparaju"));
        users.add(new User(3, "Venkat Raman"));
        users.add(new User(4, "Prabhakaran"));
    }

    // ...
}
<h:dataTable value="#{bean.users}" var="user">
    <h:column>#{user.id}</h:column>
    <h:column>#{user.name}</h:column>
</h:dataTable>
Inattentive answered 15/1, 2013 at 18:3 Comment(2)
Hi BalusC.., Entry is a Non Serializable Interface. Doesn't affect when using ViewScoped Bean. [p.s I'm using LinkedHashMap] ?Fluker
@KishorP: Nope. For future unrelated questions and/or more detailed answers, please press "Ask Question" button.Inattentive
K
4

You can try this alternative too.

<h:dataTable border="1" value="#{myBean.map.keySet().toArray()}" var="myVar">
    <h:column id="column1">
        <f:facet name="header">
            <h:outputText value="UserId"></h:outputText>
        </f:facet>
            <h:outputText value="#{myVar}"></h:outputText>
    </h:column>
    <h:column id="column2">
        <f:facet name="header">
            <h:outputText value="Email Id"></h:outputText>
        </f:facet>
            <h:outputText value="#{myBean.map.get(myVar)}"></h:outputText>
    </h:column>
</h:dataTable>
Kurd answered 16/1, 2013 at 11:56 Comment(1)
This is very performance inefficient. The EL expression in the value attribute is evaluated during every iteration round. So, map keyset is read and converted to an array during every iteration round. And, the map.get(key) does unnecessarily a second iteration over the map in order to get the value. So, you've there a nested iteration in an iteration which is executed during every iteration! You should at least use map.entrySet() instead and preferably wrap it in a fixed array list. Exactly as the currently accepted answer does.Inattentive
L
4

Regarding to the last answer from prageeth, you may use entrySet instead of keySet; then you can get rid of myBean.map.get. See this example:

<h:dataTable border="1" value="#{myBean.map.entrySet().toArray()}" var="map">
<h:column id="column1">
    <f:facet name="header">
        <h:outputText value="UserId"></h:outputText>
    </f:facet>
        <h:outputText value="#{map.key}"></h:outputText>
</h:column>
<h:column id="column2">
    <f:facet name="header">
        <h:outputText value="Email Id"></h:outputText>
    </f:facet>
        <h:outputText value="#{map.value}"></h:outputText>
</h:column>
</h:dataTable>

This works on myfaces 2.2.3 as I've just used it myself.

Annotation: I'd better had commented the last post, but my reputation is not high enough, therefore this is an extra answer.

Lynseylynus answered 17/6, 2014 at 16:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.