I'm using a view grid template and a views-view-fields--view-machine-name.html.twig template file in order to render some pictures in grid format. I want to assign dynamic CSS classes to every picture that is being rendered, for example, using the following markup.

<div class="profile-pic pic{{id}}">{{ fields.field_picture.content }}</div>

I have set a variable in Twig with the following code, but since there is no loop, it doesn't get incremented.

{% set id = 0 %}

So for every picture I have the same CSS classes (profile-pic pic0).

I want to get the following.

  • Picture 1
    profile-pic pic0
  • Picture 2
    profile-pic pic1
  • Picture 3
    profile-pic pic2
  • Picture 4
    profile-pic pic3

If I use the following code, the same imagine is rendered instead of the pictures I have.

{% for field in fields -%}
  <div class="profile-pic pic{{id}}">{{ fields.field_picture.content }}</div>
{%- endfor %}

How do I get the counter to be increased, and all the images to be rendered?


As you mentioned, there's no loop, so you can't do this in the template file (well you could, but it wouldn't be worth the effort).

Instead, you could maintain a static variable in a preprocess function:

function MYTHEME_preprocess_views_view_fields__view_machine_name(&$vars) {
  static $counter = 0;
  $vars['counter'] = $counter++;

And refer to it in the template as normal:

<div class="profile-pic pic{{ counter }}">{{ fields.field_picture.content }}</div>

You might need to tweak that, for example if the View is rendered twice on the same page.

  • 1
    I take the OP's problem is not only to get the counter increased, but also not getting the same image rendered 4 times. – kiamlaluno Jul 26 '16 at 8:35
  • 2
    Yeah I think the duplicate images are a symptom of the way the OP is trying to implement a loop in the template @kiamlaluno (when it's the parent template preprocess that would actually implement that Views-row loop) – Clive Jul 26 '16 at 8:38
  • 2
    @Clive I know that is the reason: The OP is referring the same field all the times. Is the preprocess function you are referring called for each field on the view? if it so, then probably that should be remarked, just in the case the OP didn't get it. – kiamlaluno Jul 26 '16 at 8:47
  • 2
    @NoSssweat The only difference between a static var and normal var is that it remembers its value when you leave the function, so it's the same next time the function is called. Aside from that, it can be treated as a normal variable in any PHP context – Clive Jul 26 '16 at 8:48
  • 2
    There's no way without doing some amount of preprocessing - quite simply there's no incrementing variable available to you in the context, so you need to provide one. You could do it at a more generic level, for example so that all views-fields templates have a row index in them, but there's no way to do that in Twig alone. You need PHP to provide that data, Twig will consume it. Which is exactly how it should be, Twig is for display, not logic – Clive Jul 26 '16 at 8:58

You don't need another preprocess function anymore as you can easily do this in only Twig now. But you need another template. The one you are currently using loops through all fields of one row. Not just your image field. But you want to have the template that targets exactly this one image field, where you want to loop through all items of this field's .content.

For mere demonstration purpose I'll leave that untouched for now and only show you how to increment variables in a for-loop in Twig:

{% set id = 0 %}

{% for field in fields -%}
  <div class="profile-pic pic{{ id }}">{{ fields.field_picture.content }}</div>
  {% set id = id + 1 %}
{%- endfor %}

You might also wanna have a look at the loop variable where {{ loop.index }} or {{ loop.index0 }} simply gives you the current iteration of a loop. Two more lines saved.

{% for field in fields -%}
  <div class="profile-pic pic{{ loop.index0 }}">{{ fields.field_picture.content }}</div>
{%- endfor %}

And last but not least this little gem: slice – to display a subset of items. Let's say only the first five ones.

{% for field in fields|slice(0, 5) -%}
  <div class="profile-pic pic{{ loop.index0 }}">{{ fields.field_picture.content }}</div>
{%- endfor %}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.