JSF 2 ui:repeat: group every n items inside a div
Asked Answered
B

2

6

Given a collection that I want to arrange on a page like this:

<!-- Group 0 -->
<div style="float:left;">
    <div><!-- Item 0 --></div>
    <div><!-- Item 1 --></div>

    <!-- ... -->

    <div><! -- Item n - 1 --></div>
</div>
<!-- Group 1 -->
<div style="float:left;">
    <div><!-- Item n     --></div>
    <div><!-- Item n + 1 --></div>

    <!-- ... -->

    <div><! -- Item 2n - 1 --></div>
</div>

<!-- ... -->

<!-- Group g -->
    <div><!-- Item gn     --></div>
    <div><!-- Item gn + 1 --></div>

    <!-- ... -->

    <div><! -- Item (g + 1)n - 1 --></div>
</div>

Is there some sort of trick I can use to do this inside a ui:repeat or by some other technique, preferably other than creating a custom component?

Bil answered 7/5, 2012 at 12:3 Comment(0)
I
8

You can check the current loop round by the varStatus attribute and print the intermediary </div><div style="float: left;"> whenever necessary.

E.g. every 3 items:

<div style="float: left;">
    <ui:repeat value="#{bean.list}" var="item" varStatus="loop">
        <h:outputText value="&lt;/div&gt;&lt;div style='float: left;'&gt;" escape="false" rendered="#{not loop.first and loop.index % 3 == 0}" />
        <div>#{item}</div>
    </ui:repeat>
</div>

Note that it's not possible to wrap this as plain HTML inside a <h:panelGroup>, because it would result in non-wellformed XML, hence the <h:outputText escape="false"> with them as XML entities.


Update as per the comments, here's an alternate approach having the <div>s definied only once which is probably less confusing:

<ui:repeat value="#{bean.list}" var="item" varStatus="loop">
    <h:outputText value="&lt;div style='float: left;'&gt;" escape="false" rendered="#{loop.index % 3 == 0}" />
    <div>#{item}</div>
    <h:outputText value="&lt;/div&gt;" escape="false" rendered="#{loop.last or (loop.index + 1) % 3 == 0}" />
</ui:repeat>
Inexplicable answered 7/5, 2012 at 12:13 Comment(3)
Did you notice the &lt;/div&gt; in the output text? This closes the group. I count an even number of opening and closing divs tags, not an odd number.Inexplicable
Yes, I can see what you have done, which is why I took a good, long look at it and haven't edited yet. It's clever. Problem is, you need to open the first group.Bil
Ah, the list can possibly be empty? Wrap all in <h:panelGroup rendered="#{not empty bean.list}"> then. Put it if necessary all in a composite to hide all the verbosity.Inexplicable
B
3

If possible I would break collection on the server side:

<ui:repeat value="#{groups}" var="group">
  <div style="float:left;">
    <ui:repeat value="#{group.items}" var="item">
      <div>#{item.content}</div>
    </ui:repeat>
  </div>
</ui:repeat>

another option could be (haven't tested, not sure about size behaviour in particular):

<ui:repeat value="#{items}" var="group" varStatus="status" step="n">
  <div style="float:left;">
    <ui:repeat value="#{items}" var="item" offset="#{status.index}" size="#{status.index + n}">
      <div>#{item.content}</div>
    </ui:repeat>
  </div>
</ui:repeat>

EDIT: the second version has been replaced

Bowdlerize answered 7/5, 2012 at 12:17 Comment(7)
+1 for your first solution because this would work and I thought of something similar a few minutes ago. My plan was to use a custom function to apply the grouping. I'm not sure your second solution would work, though. At the very least, any IDE will hate it.Bil
The latter works in JSP only, not in Facelets, because it's syntactically invalid XML. But in JSP you don't have <ui:xxx> tags, only a <c:forEach> and <c:if>.Inexplicable
Your updated second example doesn't result in the desired HTML output.Inexplicable
Used another approach. Should be okay now.Bowdlerize
This doesn't work if the list has size%n!=0 items. The size of the nested loop would then be bigger than actual remaining size.Inexplicable
Could be, but the question suggests size % n == 0. It's easy to adapt the code though.Bowdlerize
I've used the second solution, works like charm, just in the second ui:repeat, the size must be n : size="n"Toddy

© 2022 - 2024 — McMap. All rights reserved.