TWIG / GravCMS: Use loop-variable of for-loop inside modular template - twig

Here is my current code:
{% for module in page.collection() %}
{% set index = loop.index %}
{{ module.content|raw }}
{% endfor %}
I'd like to access index inside the module.html.twig, or even better, the entire loop variable.
How do I do that?

I found it myself:
{% for module in page.collection() if not module.header.visible is same as(false) %}
{% include module.template ~ '.html.twig' with {'page':module, 'loop':loop} %}
{% endfor %}
This loop willautomatically grab the template which is linked to the modular page and pass the required variables down. Also, the loop will only include modular subpages which are not hidden. Great, isn't it?

Related

Check if all elements in one array are contained by another | JS Array.every() equivalent

I'm looping over a collection of blog posts (Twig for loop) which appearance depends on tags given.
Here a quick example: I want to display all blog posts that have the tags "foo" and "bar".
It seems pretty easy to check if a post has one of the tags.
However it seems that checking if both tags are contained by a blog post isn't trivial.
What I want to accomplish is what the array.every() method in javascript does.
That's my current solution which works as expected but feels kinda fiddly and overcomplicated:
{% set given_tags_array = data.tags|split(',') %}
{% for post in posts %}
{% set post_categories_array = post.categories|map(category => category.name) %}
{% set bool_buffer_array = [] %}
{# push comparison result in bool array #}
{% for tag in given_tags_array %}
{% set bool_buffer_array = bool_buffer_array|merge([tag in post_categories_array]) %}
{% endfor %}
{# only display posts where all tags match #}
{% if false in bool_buffer_array %}
{% else %}
{# post data goes here #}
{% endif %}
{% endfor %}
As you can see inside the posts loop I'm checking if every given tag (i.e. "foo" and "bar") is part of the post categories array. I'm pushing the comparison result (boolean) to an empty array to check for any false values afterwards.
Why an array? I tried using a simple boolean variable but if any of the given tags is in the post categories array it resolves to true, which isn't exactly what I want.
So something like that doesn't work for me unfortunately:
{% for post in posts %}
{% set post_categories_array = post.categories|map(category => category.name)|sort|join('') %}
{% if given_tags_array|filter(given_tag => given_tag in post_categories_array) %}
{# post data goes here #}
{% endif %}
{% endfor %}
With this method I'm always doing an or comparison instead of an and comparison...
So...am I missing something and is there a simpler way to do that twig only?
Using the code you've already provided:
{% set temp = given_tags_array|filter(given_tag => given_tag in post_categories_array) %}
The filter filter returns a new array, this means temp should contain as many elements as your given_tags_array, if they are all inside the post_categories_array.
So if I'm not mistaken you could change your check to the following
{% for post in posts %}
{% set post_categories_array = post.categories|map(category => category.name)|sort|join('') %}
{% set temp = given_tags_array|filter(given_tag => given_tag in post_categories_array) %}
{% if temp|length == given_tags_array|length %}
{# display post #}
{% endif %}
{% endfor %}

How to render HTML to a variable

I need to render a list of HTML elements with content and put it into a variable. How can I do this efficient in twig?
e.g. I need to render the post tracking URLs from an order which can have several shippings / parcels.
{% for delivery in order.deliveries %}
{% for trackingCode in delivery.getTrackingCodes() %}
{{ trackingCode }}<br/>
{% endfor %}
{% endfor %}
Instead of printing this directly into the output I like first to put this rendered output into a variable like
{% set output = ... %}
...
{{ output }}
How can I do this in twig?
Just the concatenate the html to the output variable. Keep in mind you'll need to define to the variable outside the for-loop in order to use it outside the loop.
{% set foo = '' %}
{% for i in 1..10 %}
{% set foo = foo ~ ''~i~'' %}
{% endfor %}
{{ foo|raw }}
demo
After long search I found a better more efficient way
you can use {% set var %} with {% endset %} as a whole output block. Means the whole output will be set to the variable. This makes the life much easier and readable.
e.g.
{% set trackingText %}
{% for delivery in order.deliveries %}
{% for trackingCode in delivery.getTrackingCodes() %}
{{ trackingCode }}<br/>
{% endfor %}
{% endfor %}
{% endset %}
...
{% if trackingText|trim is not empty %}
You can track the delivery by using the following URL:<br/>
{{ trackingText }}
<br/>
{% endif %}

For loop in twig using variables

im trying to go through a loop multiple times and I want to use my variable to determine how many times the loop will be executed. But I cannot get it to work.
Im fairly new to Twig.
{% for item in items %}
{% for i in 1..item.content %}
<div>{{ item.content }}</div>
{% endfor %}
{% endfor %}
EDIT:
No clue what Im doing wrong. When I print item.content like this {{ item.content }} it outputs 5. But when I go trough the loop like in this example, I only get the result once

How to use Twig_Markup object type in an If statement

I want to reuse pretty heavy logic only code a few times, in php I would use a function, but in twig I went with a solution from this old question.
In short, I use a macro like that:
{% import _self as test %}
{% macro check() %}
{{ test }}
{% endmacro %}
{% set v = test.check() %}
{% if v == 'test' %}
this should display
{% endif %}
Here is a fiddle: https://twigfiddle.com/kyv3zr/2
The problem is that v is a Twig_markup object. It doesn't seem to have any public properties. Running dump on it gives me this:
object(Twig_Markup)#1244 (2) { ["content":protected]=> string(13) " 1 " ["charset":protected]=> string(5) "UTF-8" }
How do I use it in an if statement?
Or is there a better way of storing a logic only code for reuse across templates?
If the object is called v then the dump seems to show it has a content value, so try:
{% if v.content == '1' %}
{# do something here #}
{% endif %}
not certain though, but try it.
EDIT #2 - based on comments question.
So I guess if you want to use v in an if statement, you would use it like so:
{% if v == '1' %}
{# do something here #}
{% endif %}
This presumes it does equal to "1".

for loop counter with Twig or Swig

Anyone know of a clean way to do this in Twig/Swig:
{% for(i = 0; i < 100; i++) %}
blah....
{% endfor %}
If you have a number, then you can just convert this to an array and then use Swig's standard for tag. This is simplest if you always want to 'start' the loop from 0 though.
For example:
{% set productCount = 6 %}
{% set productCountAsArray = Array(productCount) %}
{# This will run productCount times #}
{% for x, y in productCountAsArray %}
This is for number: {{ x }}
{% endfor %}
The swig docs have since (ivoba's answer) been updated and now contain special loop variables, which include loop.index:
{% for x in y %}
{% if loop.first %}<ul>{% endif %}
<li>{{ loop.index }} - {{ loop.key }}: {{ x }}</li>
{% if loop.last %}</ul>{% endif %}
{% endfor %}
http://paularmstrong.github.io/swig/docs/#tags-for
For twig its:
{% for i in 0..100 %}
* {{ i }}
{% endfor %}
From http://twig.sensiolabs.org/doc/tags/for.html
For swig the docs dont mention it yet:
https://github.com/paularmstrong/swig/blob/master/docs/tags.md#for
i cant really tell but it might be not supported in swig since its django inspired and django also seems to lack this feature nativly: https://code.djangoproject.com/ticket/5172
so i would like to pass the swig part to the next one.

Resources