Twig, add first and last class

PhpSymfonyTwig

Php Problem Overview


Let's say I have a for loop like this:

{% for elem in arrMenu %}
    <div class="topmenu-button">
        <a href="{{ elem.url }}">{{ elem.name }}</a>
    </div>
{% endfor %}

In that form, it would render something like:

<div class="topmenu-button"><a href="url">name</a></div>
<div class="topmenu-button"><a href="url">name</a></div>
<div class="topmenu-button"><a href="url">name</a></div>
<div class="topmenu-button"><a href="url">name</a></div>

How can twig help me to add first and last classes the the div, so that I would have a result like:

<div class="topmenu-button first"><a href="url">name</a></div>
<div class="topmenu-button"><a href="url">name</a></div>
<div class="topmenu-button"><a href="url">name</a></div>
<div class="topmenu-button last"><a href="url">name</a></div>

Php Solutions


Solution 1 - Php

You can use the "special variables" loop.first and loop.last for what you want.

LINK

{% for elem in arrMenu %}
    {% if loop.first %}
    <div class="topmenu-button first">        
    {% elseif loop.last %}
    <div class="topmenu-button last">        
    {% else %}
    <div class="topmenu-button">        
    {% endif %}
        <a href="{{ elem.url }}">{{ elem.name }}</a>
    </div>
{% endfor %}

EDIT: depending how much you care about the "one case", you might need a solution like this.

{% for elem in arrMenu %}
    {% set classes = ["topmenu-button"] %}
    {% if loop.first %}{% set classes = classes|merge(["first"]) %}{% endif %}
    {% if loop.last %}{% set classes = classes|merge(["last"]) %}{% endif %}
    <div class="{{ classes|join(" ") }}">        
        <a href="{{ elem.url }}">{{ elem.name }}</a>
    </div>
{% endfor %}

Solution 2 - Php

Since a loop can't be first and last at the same time, i would prefer not to use elseif and write (DRY - what if you have to change topmenu-button in future?):

{% for elem in arrMenu %}
    {% set append %}
        {% if loop.first %}first{% endif %}
        {% if loop.last %}last{% endif %}
    {% endset %}
    <div class="topmenu-button {{ append }}">
        <a href="{{ elem.url }}">{{ elem.name }}</a>
    </div>
{% endfor %}

or more concise:

{% for elem in arrMenu %}
    <div class="topmenu-button {% if loop.first %}first{% endif %} {% if loop.last %}last{% endif %}">
        <a href="{{ elem.url }}">{{ elem.name }}</a>
    </div>
{% endfor %}

Edit: this way you'll get some extra spaces in class attribute (it's perfectly fine btw). Edit2: this will not handle 1-element array (first = last)

Solution 3 - Php

If it's to manage class attribut of a tag, a ternary condition would be better.

<span class="topmenu-button {{ (loop.first) ? 'first' : '' }}">{{ item.text }}</span>

Solution 4 - Php

I use this in Drupal 8 views template. Thanks for 'elseif', if you have only 1 item, then the first item never get the 'last' class:

{% for row in rows %}
	{% set parity = cycle(['odd', 'even'], loop.index0) %}
	{% set row_classes = [ default_row_class ? 'views-row',	] %}	

	<div class="views-prerow {{parity}}{% if loop.first %} first{% elseif loop.last %} last{% endif %}">
		<div{{ row.attributes.addClass(row_classes) }}>
			{{ row.content }}
		</div>
	</div>
{% endfor %}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFMaz008View Question on Stackoverflow
Solution 1 - PhpKendall HopkinsView Answer on Stackoverflow
Solution 2 - PhpgremoView Answer on Stackoverflow
Solution 3 - PhpPaul LeclercView Answer on Stackoverflow
Solution 4 - PhpGódi PéterView Answer on Stackoverflow