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?

Steve
  • 8,066
  • 11
  • 70
  • 112

2 Answers2

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>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Did you notice the `</div>` in the output text? This closes the group. I count an even number of opening and closing divs tags, not an odd number. – BalusC May 07 '12 at 12:29
  • 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. – Steve May 07 '12 at 12:33
  • Ah, the list can possibly be empty? Wrap all in `` then. Put it if necessary all in a composite to hide all the verbosity. – BalusC May 07 '12 at 12:34
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

mrembisz
  • 12,722
  • 7
  • 36
  • 32
  • +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. – Steve May 07 '12 at 12:26
  • The latter works in JSP only, not in Facelets, because it's syntactically invalid XML. But in JSP you don't have `` tags, only a `` and ``. – BalusC May 07 '12 at 12:26
  • Your updated second example doesn't result in the desired HTML output. – BalusC May 07 '12 at 12:32
  • Used another approach. Should be okay now. – mrembisz May 07 '12 at 13:06
  • 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. – BalusC May 07 '12 at 13:07
  • Could be, but the question suggests size % n == 0. It's easy to adapt the code though. – mrembisz May 07 '12 at 13:11
  • I've used the second solution, works like charm, just in the second ui:repeat, the size must be n : size="n" – Hossein Feb 15 '15 at 13:47