Theme colour switcher

How to limit number of rows in each group in a grouped View using Twig

I wanted to create a view of "event" content type, grouped by taxonomy term. For each group, I wanted to show only the first 4 items, with a "more" link to see all.

This seems like it should be a simple task, but isn't, and isn't possible out of the box with views.

I didn't want to have to create a view display for every taxonomy term. That's a huge pain, requires maintenance every time a new term is added, and doesn't allow you to do things like sort by taxonomy term weight.

I looked for solutions and didn't really find many options. There is one tutorial on how to do it in a custom module and an abandoned contrib module that only made it to Drupal 7. Update: there's also the Views Complex Grouping module which claims to do this, but isn't currently covered by the security advisory policy.

I guess I could have gone the custom module route, but decided to try it in Twig instead. One important note: I tried looping over the items in each group using this type of "for" loop:

{% for key, row in rows %}
    {% if key < 5 %}
        {{- row.content -}}
    {% endif %}
{% endfor %}

And it didn't work. Keys gave me all of the total rows in the view, not only the rows in each group. So this limited the view to only the first 4 items in the whole view, not in each group.

Then I found that loop.index would do what I needed, returning the index of the items in each group and resetting for each group (credit: StackOverflow).

This is what I ended up with, in a custom views-view--unformatted.html.twig file (simplified to show only the essentials). I'm limiting the number of items in each group to 4 and showing a "more" link based on the taxonomy term each group's content is tagged with. It's a hacky way to do things, probably not the best way, and I don't know how it would perform if you have a view with many many items (I wouldn't recommend trying it). This particular view will never show a large number of items, so it should perform ok. Note that in your view you will have to display all items and not use a pager.

Unfortunately, for the "more" link, to get data from the rows to link to the correct category, I had to do another loop over the rows and limit it to show only one. As I said, hacky.

<div class="event-category--section">
  <h2>{{ title }}</h2>

  <div class="event-cards">
    {% for row in rows %}
      {%
        set row_classes = [
          default_row_class ? 'event-card',
        ]
      %} 
      {% if loop.index < 5 %}
        <div{{ row.attributes.addClass(row_classes) }}>
          {{- row.content -}}
        </div>
      {% endif %}
    {% endfor %}
  </div>
  {% for row in rows %}
      {% if rows|length > 4 and loop.index == 1 %}
        <p>
          <a href="/events/all?category={{ row.content['#row']._entity.field_category[0].entity.tid.value }}">
              {% trans %}All {{ row.content['#row']._entity.field_category[0].entity.name.value }} events{% endtrans %}
          </a>
        </p>
      {% endif %}
  {% endfor %}
</div>

Tags:

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.