Using EL variable of <c:set> inside Facelets <ui:repeat> tags

China☆狼群 提交于 2020-05-11 08:23:07

问题


I have a Home. Each Home has a list of Rooms. Each Room has zero or more Persons.

I would like to count total persons of each home. But I can't add a new variable to record person count in any backing beans or entity. So I'd like to count it in the view only via <c:set>.

My first attempt look like:

<c:set var="personCount" value="${0}" />
<ui:repeat var="home" value="#{mybackingBean.homes}">
    <ui:repeat var="room" value="#{home.rooms}">
        ${personCount += room.persons.size()}
    </ui:repeat>
    <h:panelGrid columns="2">
        <h:outputLabel value="#{home.id}" />
        <h:outputLabel value="#{personCount}" />
    </h:panelGrid>
</ui:repeat>

How can I achieve it?


回答1:


There are several mistakes.


First,

${personCount += room.persons.size()}

In EL, the += is the string concatenation operator. So, this will give you basically the result of String.valueOf(personCount) + String.valueOf(size). If the count is initialized to 0 and there are 3 persons, then this will print 03.

You basically want to re-assign the result of personCount + size back to variable personCount. You need to re-set the variable using <c:set> as below.

<c:set var="personCount" value="${personCount + room.persons.size()}" />

Second,

<c:set var="personCount" value="${0}" />
<[for each home]>
    <[for each room]>
        <c:set var="personCount" value="${personCount + room.persons.size()}" />

The personCount is initialized with 0 outside both iterations. So, for each home you'll this way count the persons of all previous homes as well. You want to declare it inside the first iteration.

<[for each home]>
    <c:set var="personCount" value="${0}" />
        <[for each room]>
            <c:set var="personCount" value="${personCount + room.persons.size()}" />

This is not EL related. This is just a logic error which would cause the same problem in "plain vanilla" Java as well.


Third,

JSTL tags like <c:set> run during view build time (during conversion from XHTML template to JSF component tree). JSF components like <ui:repeat> run during view render time (during conversion from JSF component tree to HTML output). So, the <c:set> doesn't run during <ui:repeat> iteration. Thus, it cannot see the iteration variable var. You basically need to iterate during view build time as well. You can use JSTL <c:forEach> for this.

So, all in all, your intented solution look like this:

<c:forEach items="#{bean.homes}" var="home">
    <c:set var="personCount" value="${0}" />
    <c:forEach items="#{home.rooms}" var="room">
        <c:set var="personCount" value="${personCount + room.persons.size()}" />
    </c:forEach>

    <h:panelGrid columns="2">
        <h:outputLabel value="#{home.id}" />
        <h:outputLabel value="#{personCount}" />
    </h:panelGrid>
</c:forEach>

For an in depth explanation, see also JSTL in JSF2 Facelets... makes sense?


However, as the += operator apparently doesn't cause an EL exception in your case, this means that you're already using EL 3.0. This in turn means that you can use the new EL 3.0 lambda and stream features.

<ui:repeat value="#{bean.homes}" var="home">
    <h:panelGrid columns="2">
        <h:outputLabel value="#{home.id}" />
        <h:outputLabel value="#{home.rooms.stream().map(room -> room.persons.size()).sum()}" />
    </h:panelGrid>
</ui:repeat>

No need for a counting loop anymore. No, this does not require Java 8, it works on Java 7 as good.



来源:https://stackoverflow.com/questions/32390021/using-el-variable-of-cset-inside-facelets-uirepeat-tags

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!