在h:dataTable中使用java.util.Map

18

我需要使用<h:dataTable>来显示Map。我的后端bean有如下的Map属性:

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");
    }

}

然后在JSF页面中,我试图将这个Map属性绑定到<h:dataTable>value属性。

 <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>

它报错说getKeygetValue不存在。我可以理解这不是正确的做法。如何使用<h:dataTable>呈现Map?

3个回答

31

直到即将推出的JSF 2.3版本,像<h:dataTable><p:dataTable>UIData组件和<ui:repeat>不支持迭代Map。这仅在<c:forEach>中支持。

一种方法是将地图条目转换为数组(仅entrySet()不能工作,因为UIData在即将发布的JSF 2.3之前也不支持Set)。

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

另一种方法是将地图的条目集包装在一个集合中,<h:dataTable> 可以遍历该集合,例如 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>

然而,更清晰、自文档化和可重用的方法是使用一个 List<User>,其中 User 类具有必要的属性 idname

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>

嗨,BalusC..,“Entry”是一个非“Serializable”接口。在使用“ViewScoped” Bean时不会受到影响。[附言:我正在使用“LinkedHashMap”]? - Kishor Prakash
@KishorP:不是的。如果有未来无关的问题和/或需要更详细的答案,请按“提问”按钮。 - BalusC

4
您也可以尝试这个替代方案。
<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>

2
这非常低效。value 属性中的 EL 表达式在每次迭代循环中都会被评估。因此,映射键集在每次迭代循环中都会被读取并转换为数组。而且,map.get(key) 不必要地进行了第二次迭代,以获取值。因此,在每次迭代中执行了一个嵌套迭代!你至少应该使用 map.entrySet(),最好将其包装在一个固定的数组列表中。就像当前接受的答案一样。 - BalusC

4

关于prageeth的最后一个答案,您可以使用entrySet而不是keySet;然后您就可以摆脱myBean.map.get。请看这个例子:

<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>

这个在myfaces 2.2.3上可以运行,我刚刚自己使用了一下。
注释:我最好对上一篇帖子进行评论,但是我的声望还不够高,因此这是一个额外的答案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接