Conditional image rendering using JSTL

2019-08-02 12:11发布

问题:

I`m developing a JSF application and I have a question regarding a Facelets page with JSTL. I want to display in an ui:repeat a number of question, a question and 3 answers (formated conditionally) and in front of the answers a tick (tickSmall.png) or an X (xSmall.png) if the questions are right or wrong.

The answers are formated correctly, but the tick / X is not put correctly (I checked the booleans are correct, some are true, some are false). EVERY time it puts an X, even if there should be a tick.

I have included xmlns:c="http://java.sun.com/jsp/jstl/core

The code:

     <ui:repeat var="n"
                    value="#{answerResultBean.accessWrongAnswerColumnWrapperList}">
                    <hr />
                    <h:outputText value="#{n.noQuestion}. #{n.question}" />
                    <h:panelGrid columns="2" style="text-align: left;">
                        <c:choose>
                            <c:when test="#{n.rightGridAnswerA}">
                                <h:form>
                                    <img src="/juritest/resources/img/tickSmall.png" alt="tick" />
                                </h:form>
                            </c:when>
                            <c:otherwise>
                                <h:form>
                                    <img src="/juritest/resources/img/xSmall.png" alt="wrong" />
                                </h:form>
                            </c:otherwise>
                        </c:choose>
                        <h:outputText value="a) #{n.a}"
                            style="#{n.userAnswerA ? 'font-weight:bold;' : 'font-weight:normal;'}" />

                        <c:choose>
                            <c:when test="#{n.rightGridAnswerB}">
                                <h:form>
                                    <img src="/juritest/resources/img/tickSmall.png" alt="tick" />
                                </h:form>
                            </c:when>
                            <c:otherwise>
                                <h:form>
                                    <img src="/juritest/resources/img/xSmall.png" alt="wrong" />
                                </h:form>
                            </c:otherwise>
                        </c:choose>
                        <h:outputText value="b) #{n.b}"
                            style="#{n.userAnswerB ? 'font-weight:bold;' : 'font-weight:normal;'}" />

                        <c:choose>
                            <c:when test="#{n.rightGridAnswerC}">
                                <h:form>
                                    <img src="/juritest/resources/img/tickSmall.png" alt="tick" />
                                </h:form>
                            </c:when>
                            <c:otherwise>
                                <h:form>
                                    <img src="/juritest/resources/img/xSmall.png" alt="wrong" />
                                </h:form>
                            </c:otherwise>
                        </c:choose>
                        <h:outputText value="c) #{n.c}"
                            style="#{n.userAnswerC ? 'font-weight:bold;' : 'font-weight:normal;'}" />
                    </h:panelGrid>
                </ui:repeat>

回答1:

JSTL tags and JSF components doesn't run in sync as you'd expect from the coding. JSTL tags runs first during view build time, producing the JSF component tree. JSF components runs thereafter during view render time, producing HTML output. So, when JSTL runs, the #{n} is nowhere available in the EL scope, because the <ui:repeat> hasn't run at that moment.

You need JSF component's rendered attribute instead, or make use of the ternary operator in EL the smart way.

The following blocks of clumsiness

<c:choose>
    <c:when test="#{n.rightGridAnswerA}">
        <h:form>
            <img src="/juritest/resources/img/tickSmall.png" alt="tick" />
        </h:form>
    </c:when>
    <c:otherwise>
        <h:form>
            <img src="/juritest/resources/img/xSmall.png" alt="wrong" />
        </h:form>
    </c:otherwise>
</c:choose>
...
<c:choose>
    <c:when test="#{n.rightGridAnswerB}">
        <h:form>
            <img src="/juritest/resources/img/tickSmall.png" alt="tick" />
        </h:form>
    </c:when>
    <c:otherwise>
        <h:form>
            <img src="/juritest/resources/img/xSmall.png" alt="wrong" />
        </h:form>
    </c:otherwise>
</c:choose>
...
<c:choose>
    <c:when test="#{n.rightGridAnswerC}">
        <h:form>
            <img src="/juritest/resources/img/tickSmall.png" alt="tick" />
        </h:form>
    </c:when>
    <c:otherwise>
        <h:form>
            <img src="/juritest/resources/img/xSmall.png" alt="wrong" />
        </h:form>
    </c:otherwise>
</c:choose>

can be rewritten as follows using the rendered attribute:

<h:form>
    <h:graphicImage name="img/tickSmall.png" alt="tick" rendered="#{n.rightGridAnswerA}" />
    <h:graphicImage name="img/xSmall.png" alt="wrong" rendered="#{not n.rightGridAnswerA}" />
</h:form>
...
<h:form>
    <h:graphicImage name="img/tickSmall.png" alt="tick" rendered="#{n.rightGridAnswerB}" />
    <h:graphicImage name="img/xSmall.png" alt="wrong" rendered="#{not n.rightGridAnswerB}" />
</h:form>
...
<h:form>
    <h:graphicImage name="img/tickSmall.png" alt="tick" rendered="#{n.rightGridAnswerC}" />
    <h:graphicImage name="img/xSmall.png" alt="wrong" rendered="#{not n.rightGridAnswerC}" />
</h:form>

or as follows using the conditional operator:

<h:form>
    <h:graphicImage name="img/#{n.rightGridAnswerA ? 'tick' : 'x'}Small.png" alt="#{n.rightGridAnswerA ? 'tick' : 'wrong'}" />
</h:form>
...
<h:form>
    <h:graphicImage name="img/#{n.rightGridAnswerB ? 'tick' : 'x'}Small.png" alt="#{n.rightGridAnswerB ? 'tick' : 'wrong'}" />
</h:form>
...
<h:form>
    <h:graphicImage name="img/#{n.rightGridAnswerC ? 'tick' : 'x'}Small.png" alt="#{n.rightGridAnswerC ? 'tick' : 'wrong'}" />
</h:form>

(which is still not DRY, but that's a different problem)

See also:

  • JSTL in JSF2 Facelets... makes sense?
  • How to reference CSS / JS / image resource in Facelets template?