Twig batch and loop index - symfony

I have a Twig for loop using the batch filter to wrap every 2 elements in a container div. I want to add a classname to every 3rd and 4th div in this for loop. However it seems you can't use loop.index when using the batch filter. Is that correct? How do you access the index then when using the batch filter?
What I tried is this:
{% for batch in blog.articles | limit(6) | batch(2) %}
<div class="blog-art-wrap row-eq-height">
{% for article in batch %}
<div class="article {% if loop.index == 3 or loop.index == 4 %}some-class{% endif %}">...... </div>
{% endfor %}
</div>
{% endfor %}
I also tried it with loop.index3 etc... But it just seems to ignore this.
Or is this because of the batches are in 2? So there's actually no index 3 and 4?? If so how do you access every 2nd batch then?

You could use the parent loop variable in order to refer to the parent context. As example:
{% for batch in blog.articles | batch(2) %}
<div class="blog-art-wrap row-eq-height">
{% for article in batch %}
<div class="article {% if loop.parent.loop.index == 3 or loop.parent.loop.index == 4 %}some-class{% endif %}">...... </div>
{% endfor %}
</div>
{% endfor %}
Try in this working twigfiddle.
Hope this help

try this:
{% set className = '' %}
{% if loop.index is divisibleby(3) or loop.index is divisibleby(4) %}
{% set className = 'some-class' %}
{% endif %}
<div class="article {{ className }}">...... </div>

Related

Using a counter in a Twig node template

Setting up a Drupal 8 custom view for Top 4 items, I want a different layout for item 1 than the remaining. I have override files for the custom view, but for this example I'm using base files to keep it simple.
In views-view.html.twig base file we have:
<div class="view-content">
{{ rows }}
</div>
In node.html.twig base file we have:
<div {{ content_attributes.addClass('content') }}>
{{ content }}
</div>
In node.html.twig I am aiming for something like:
{% if row_counter = 1 %}
output this markup / fields
{% else %}
do something boring with the other 3 items.
{% endif %}
I was able to set row_counter in the views-view.twig.html file:
{% for row in rows %}
{% set row_counter = loop.index %}
<div{{ row.attributes }}>
{{ row_counter }}
</div>
{% endfor %}
But I need to check against the value of {{ row_counter }} in the node.html.twig file....
What other properties are available in node.html.twig to check against its position in the list?
From the documentation
The loop variable
Inside of a for loop block you can access some special variables:
Variable Description
-----------------------------------------------------------------
loop.index The current iteration of the loop. (1 indexed)
loop.index0 The current iteration of the loop. (0 indexed)
loop.revindex The number of iterations from the end of the loop (1 indexed)
loop.revindex0 The number of iterations from the end of the loop (0 indexed)
loop.first True if first iteration
loop.last True if last iteration
loop.length The number of items in the sequence
loop.parent The parent context
edit: every variable know in the parent template is also known inside an include as context is passed by default. Only macro's don't know the parent's context
controller
<?php
require __DIR__ . '/../requires/propel_standalone.php';
echo $twig->render('tests/items.html', [
'items' => [
'Abc',
'def',
'ghi',
'jkl',
'mno',
'pqr',
'stu',
'vwx',
'z',
],
]);
items.twig
<!doctype>
<html>
<head><title>Test</title></head>
<body>
{% for item in items %}
{% include "tests/item.html" %}
{% endfor %}
</body>
</html>
item.twig
{% set order = 'Nothing to report' %}
{% if loop.first %}
{% set order = 'I\'m first' %}
{% endif %}
{% if loop.last %}
{% set order = 'I\'m last' %}
{% endif %}
{% if loop.index is even %}
{% set order = 'I\'m even' %}
{% endif %}
{% if loop.index is divisible by(5) %}
{% set order = 'I can be dived by 5' %}
{% endif %}
{% if loop.index is divisible by(3) %}
{% set order = 'I can be dived by 3' %}
{% endif %}
<div>
<b>{{ loop.index }}:</b>{{ order }} - {{ item }}
</div>
You could do something like this if a class is enough:
Your views template:
{% for row in rows %}
<div class="{% if loop.index == 1 %}first_element{% endif %}">
{{ row.content }}
</div>
{% endfor %}
And then just style it appropriately.

Twig, how to print a nl2br output in one line

I used the nl2br filter like this:
{{ knp_pagination_render(requests)|raw|nl2br}}
It prints the html object separated by a few line breaks, and I need to print all the objects in one single line. How can I reach that?
Any help would be great.
{{ string | replace({"\\n":""}) }}
See fiddle.
I suggest you to override the default pagination template, so you can customize how to generate the underling HTML code.
As described in the doc You can do simply:
{{ knp_pagination_render(request, 'MyBundle:Pagination:pagination.html.twig') }}
You can copy the current pagination implementation from here and customize it as you need.
This is the default implementation:
{# default Sliding pagination control implementation #}
{% if pageCount > 1 %}
<div class="pagination">
{% if first is defined and current != first %}
<span class="first">
<<
</span>
{% endif %}
{% if previous is defined %}
<span class="previous">
<
</span>
{% endif %}
{% for page in pagesInRange %}
{% if page != current %}
<span class="page">
{{ page }}
</span>
{% else %}
<span class="current">{{ page }}</span>
{% endif %}
{% endfor %}
{% if next is defined %}
<span class="next">
>
</span>
{% endif %}
{% if last is defined and current != last %}
<span class="last">
>>
</span>
{% endif %}
</div>
{% endif %}

Twig separation if statement

I have in template 10-20 same if statements.
Example:
{% if a == b %}
<div>text</div>
{% endif %}
other code
{% if a == b %}
<span></span>
{% endif %}
other code
{% if a == b %}
<div>text 2</div>
{% endif %}
and now if I need change condition I must change it in several places.
How can I easy separate this condition and change only in one place?
You can save the conditions result in a variable:
{% set ab_cond = a == b %}
{% if ab_cond %}
<div>text</div>
{% endif %}
other code
{% if ab_cond %}
<span></span>
{% endif %}
other code
{% if ab_cond %}
<div>text 2</div>
{% endif %}
Calculate it once, store the result as a variable, use the variable in the if statements.

Twig: Loop Index and Second Value for each Iteration

i got the following loop
{% set services = { "ceoCentralServices": ceoCentralServices, "cfoCentralServices": cfoCentralServices, "cooCentralServices": cooCentralServices} %}
{% for events, serviceEvents in services %}
{% if serviceEvents %}
<div class="wrapItFine" style="background:purple;">
{% for event in serviceEvents %}
<div class="dialog" data-index="loop2{{ loop.index0 }}">
<li class="contentli">{{ event.value }}</li>
</div>
<div style="display:none;"
id="anmelden_boxloop2{{ counter }}{{ loop.index0 }}"
class="{{ event.value }}{{ loop.index0 }}" >
{% include 'ansprechpartnerSingle.twig' %}
</div>
{% endfor %}
</div>
{% endif %}
{% endfor %}
this returns 3 boxes with data-index="loop2+index.
the problem is, that i need in each loop a different value like loop2+index, loop3+index, loop4+index
i tried a set Counter and incrementing in the for loop which returned every time the same value.
feel free to downvote, like everytime :-)
in your second loop you can simply use
{{ loop.parent.loop.index0 }}
to get the index of the parent loop, which you can use instead of your counter
data-index="loop{{ loop.parent.loop.index0 }}{{ loop.index0 }}"

Jekyll automatically processing rows

I want to filter out from site.pages all pages with a given YAML frontmatter type 'project'.
To do this I've done this:
{% sorted_for page in site.pages sort_by:title %}
{% if page.type == 'project' %}
do something
{% endif %}
{% endfor %}
This uses the sorted_for plugin. What I want to do now, however is to arrange these in css rows of four.
<div class="row">
<div class="span3">{{ page1.title }}</div>
<div class="span3">{{ page2.title }}</div>
<div class="span3">{{ page3.title }}</div>
<div class="span3">{{ pagen.title }}</div>
</div>
...
And so forth, until the rows run out. How can I access the information I need to do this? Unfortunately the for loop iterator number will be wrong, as it will also tick through the non-project pages. Is there a clean way to implement this?
Note:
I couldn't get the sorted_for plugin to work in my machine, so I tested my solution with a regular for instead.
Step 1
As you can't use forloop.index because you're filtering out some pages, you need to count the loops by yourself, by writing to a variable with assign.
The following code will just list your pages with a correct loop iterator (by counting just the pages that are actually listed):
{% assign loopindex = 0 %}
{% for page in site.pages %}
{% if page.type == 'project' %}
{% assign loopindex = loopindex | plus: 1 %}
<div class="span3">{{ loopindex }} {{ page.title }}</div>
{% endif %}
{% endfor %}
Step 2
You need to display <div class="row"> with every first row and </div> with every fourth row.
To find the first and fourth rows, you can use modulo:
{% assign loopindex = 0 %}
{% for page in site.pages %}
{% if page.type == 'project' %}
{% assign loopindex = loopindex | plus: 1 %}
{% assign rowfinder = loopindex | modulo: 4 %}
<div class="span3">{{ loopindex }} {{ rowfinder }} {{ page.title }}</div>
{% endif %}
{% endfor %}
rowfinder will always repeat the sequence 1, 2, 3, 0.
Step 3:
So you display <div class="row"> when rowfinder is 1, and </div> when rowfinder is 0:
{% assign loopindex = 0 %}
{% for page in site.pages %}
{% if page.type == 'project' %}
{% assign loopindex = loopindex | plus: 1 %}
{% assign rowfinder = loopindex | modulo: 4 %}
{% if rowfinder == 1 %}
<div class="row">
<div class="span3">{{ page.title }}</div>
{% elsif rowfinder == 0 %}
<div class="span3">{{ page.title }}</div>
</div>
{% else %}
<div class="span3">{{ page.title }}</div>
{% endif %}
{% endif %}
{% endfor %}
Step 4:
Now there's only one small thing left: when the number of pages is not a multiple of 4, there's a </div> missing at the end.
When the number of pages is a multiple of 4, the last value of rowfinder will be 0.
So we just need to display the </div> when the value of rowfinder is anything else but 0.
So just put this after the for loop:
{% if rowfinder != 0 %}
</div>
{% endif %}
...and that's it!

Resources