Specify conditional rendering of element inside <ui:repeat>? The <c:if> does not seem to work

I am trying to conditionally build a custom list using <ui:repeat>. On every occurrence of -1 as item-value in list, I need to add a line break.

I tried to use <c:if> inside <ui:repeat> for that, but it does not seem to work. It always evaluates false.

<ul>      
    <ui:repeat value="#{topics.list}" var="topicId" >
        <li>#{topicId}</li>
        <c:if test="#{topicId eq -1}">  <br/>  </c:if>
    </ui:repeat>
</ul>

Is this possible?


Not with JSTL tags, no. They run during view build time, not during view render time. You can visualize it as follows: when JSF builds the view, JSTL tags run from top to bottom first and the result is a pure JSF component tree. Then when JSF renders the view, JSF components run from top to bottom and the result is a bunch of HTML. So, JSTL and JSF don't run in sync as you'd expect from the coding. At the moment your <c:if> JSTL tag tag runs, the #{topicId} variable which is set by <ui:repeat> JSF component isn't available in the scope.

Instead of using <c:if>, you need to specify the condition in the rendered attribute of the JSF component of interest. As you've actually none, you could wrap it in a <ui:fragment>.

<ul>      
    <ui:repeat value="#{topics.list}" var="topicId" >
        <li>#{topicId}</li>
        <ui:fragment rendered="#{topicId eq -1}"><br/></ui:fragment>
    </ui:repeat>
</ul>

Alternatives are <h:panelGroup>

<h:panelGroup rendered="#{topicId eq -1}"><br/></h:panelGroup>

or in your specific case <h:outputText escape="false">

<h:outputText value="&lt;br/&gt;" escape="false" rendered="#{topicId eq -1}" />

as both also emits nothing else to the HTML output when no client side attributes are specified.

See also:

  • JSTL in JSF2 Facelets... makes sense?

Unrelated to the concrete problem, that's the wrong place for a <br/>. It would be ignored by any webbrowser respecting the HTML specification. Don't you mean it to be inside the <li>? Or better, give it a class and let CSS give it a margin-bottom.