select previous item in twig for loop - for-loop

I use twig and have some data in array. I use for loop to access all data like this:
{% for item in data %}
Value : {{ item }}
{% endfor %}
Is it possible to access previous item in loop? For example: when I'm on n item, I want to have access to n-1 item.

There's no built-in way to do that, but here's a workaround:
{% set previous = false %}
{% for item in data %}
Value : {{ item }}
{% if previous %}
{# use it #}
{% endif %}
{% set previous = item %}
{% endfor %}
The if is neccessary for the first iteration.

In addition to #Maerlyn 's example, here is code to provide for next_item (the one after current one)
{# this template assumes that you use 'items' array
and each element is called 'item' and you will get
'previous_item' and 'next_item' variables, which may be NULL if not availble #}
{% set previous_item = null %}
{%if items|length > 1 %}
{% set next_item = items[1] %}
{%else%}
{% set next_item = null %}
{%endif%}
{%for item in items %}
Item: {{ item }}
{% if previous_item is not null %}
Use previous item here: {{ previous_item }}
{%endif%}
{%if next_item is not null %}
Use next item here: {{ next_item }}
{%endif%}
{# ------ code to udpate next_item and previous_item elements #}
{%set previous_item = item %}
{%if loop.revindex <= 2 %}
{% set next_item = null %}
{%else%}
{% set next_item = items[loop.index0+2] %}
{%endif%}
{%endfor%}

My solution:
{% for item in items %}
<p>item itself: {{ item }}</p>
{% if loop.length > 1 %}
{% if loop.first == false %}
{% set previous_item = items[loop.index0 - 1] %}
<p>previous item: {{ previous_item }}</p>
{% endif %}
{% if loop.last == false %}
{% set next_item = items[loop.index0 + 1] %}
<p>next item: {{ next_item }}</p>
{% endif %}
{% else %}
<p>There is only one item.</p>
{% endif %}
{% endfor %}
I had to make endless image gallery where before first item goes last one and after last item goes first one. It could be done this way:
{% for item in items %}
<p>item itself: {{ item }}</p>
{% if loop.length > 1 %}
{% if loop.first %}
{% set previous_item = items[loop.length - 1] %}
{% else %}
{% set previous_item = items[loop.index0 - 1] %}
{% endif %}
{% if loop.last %}
{% set next_item = items[0] %}
{% else %}
{% set next_item = items[loop.index0 + 1] %}
{% endif %}
<p>previous item: {{ previous_item }}</p>
<p>next item: {{ next_item }}</p>
{% else %}
<p>There is only one item.</p>
{% endif %}
{% endfor %}

Related

How to show name specific tag in shopify

I want to display the value of a specific product tag in my shopify store.
The product tag always starts with Color_ followed by the color Color_Green
I want to display only green. (I can use split or slice for that part)
But I can't figure out to only display the tag that starts with Color_ because I have multiple tags added to my products.
This is my code for now:
{%- for tag in product.tags -%} {%- assign tag_prefix = tag %} {%- if tag_prefix == "Color_" -%} {%- endif -%} {%- endfor -%}
Try this snippet
{% for tag in product.tags %}
{% assign tag_prefix = tag %}
{% if tag_prefix contains "Color_" %}
{% assign tagValue = tag_prefix | replace: "Color_", "" %}
{{ tagValue }}
{% endif %}
{% endfor %}

Jinja conditional concatenation for html attribute

I have the below jinja code to conditionally concatenate strings depending on two variables. Whilst it (probably) works it looks unwieldy, but I'd have thought this was a common thing to do when rendering.
{% if item.route %}
{% if item.route_follow and item.route_external %} {% set route_rel = 'external' %}
{% elif not item.route_follow and not item.route_external %} {% set route_rel = 'nofollow' %}
{% elif not item.route_follow and item.route_external %} {% set route_rel = 'nofollow external' %}
{% else %} {% set route_rel = '' %}
{% endif %}
{% endif %}
Is there a better way, maybe a one-liner?
When I got to the end of this I realised the answer:
{% if item.route %}
{% set route_rel = ['' if item.route_follow else 'nofollow', 'external' if item.route_external else '']|reject('==','')|join(' ') %}
{% endif %}
Sometimes, you just have to look at it again...

Cannot get index of item in array with Nunjucks for loop

I'm having some trouble getting indexes of a items in an array from a Nunjucks {% for %} loop.
The array I am targeting is simple and looks like this
pages[1,2,3]
And this is the Nunjucks loop
{% for i,p in data.tickets.pages %}
{{ i }} : {{ p }}
{% endfor %}
The problem is
{{ p }} outputs 1,2,3 but {{ i }} doesn't output anything.
If anyone can tell me how to fix this, I'd much appreciate it.
To get the index in a for loop use loop.index (loop.index starts with a 1)
To get the standard behavior (start with a 0) use loop.index0
data.tickets.pages = [1, 2, 3];
Template code (loop.index)
{% for page in data.tickets.pages %}
{{loop.index}}: {{ page }}
{% endfor %}
Output
1:1
2:2
3:3
Template code (loop.index0)
{% for page in data.tickets.pages %}
{{loop.index0}}: {{ page }}
{% endfor %}
Output
0:1
1:2
2:3
See Also the nunjucks docs
loop.index: the current iteration of the loop (1 indexed)
loop.index0: the current iteration of the loop (0 indexed)
loop.revindex: number of iterations until the end (1 indexed)
loop.revindex0: number of iterations until the end (0 based)
loop.first: boolean indicating the first iteration
loop.last: boolean indicating the last iteration
loop.length: total number of items
Tipically nunjucks wait single iterator for array.
When you use multi-iterator and pass array, nunjucks split each array element by iterator set.
{% set pages = [[10, 11], [12, 13]] %}
{% for a, b in pages %}
{{a}},{{b}}
{% endfor %}
---
10:11
12:13
You can use range, convert array to object (element order can be lost) or use loop.index0'/loop.index
var nunjucks = require('nunjucks');
var env = nunjucks.configure();
// range
var res = nunjucks.renderString(`
{% for i in range(0, items.length) %}
{% set item = items[i] %}
{{i}}: {{item}}
{% endfor %}`,
{items: [10, 12]}
);
console.log(res);
// array to object
res = nunjucks.renderString(`
{% for i, item in items %}
{{i}}: {{item}}
{% endfor %}`,
{items: Object.assign({}, [10, 12])}
);
console.log(res);
// loop.index0
res = nunjucks.renderString(`
{% for item in items %}
{{loop.index0}}: {{item}}
{% endfor %}`,
{items: [10, 12]}
);
console.log(res);
For anyone coming here by ways of Google or whatever. I was able to use the method with range loop described above directly in my template, and even nesting loops.
Here's part of my code directly in a nunjucks template:
<ul class="index">
{% for x in range(0, dbReport.sections.length) %}
{% set section = dbReport.sections[x] %}
<li>
<strong>{{ x + 1 }}</strong> {{ section.sectionTitle }}
<ul>
<li>
<strong>{{ x + 1 }}.1</strong> Section components:
<ul>
{% for y in range(0, section.subSections.length) %}
{% set subSection = section.subSections[y] %}
<li>
<strong>{{ x + 1 }}.1.{{ y + 1 }}</strong> {{subSection.subSectionTitle}}
</li>
{% endfor %}
</ul>
</li>
</ul>
</li>
{% endfor %}
</ul>
with array.entries()
{%- for pageIndex, page in collections.pages.entries() -%}
<div class="page" id="page-{{ pageIndex + 1 }}">
{{ page.templateContent | safe }}
</div>
{%- endfor -%}

How do I show a tag to represent multiple products? Shopify Liquid

Hello and thanks for reading my post!
I have a collection with multiple products. On a custom collection template, I want to show the tags only for those that contain multiple products (or when more than 1 product in that collection have the same tag)
I assume it would go something like:
{% for tag in collection.all_tags %}
{% if tag.product.size >= 1 %}
has more than 1 product.
{% endif %}
{% endfor %}
I've answered similar questions here and here.
You want something like this:
{% for tag in collection.all_tags %}
{% assign products_count = 0 %}
{% for product in collection.products %}
{% if product.tags contains tag %}
{% assign products_count = products_count | plus: 1 %}
{% endif %}
{% endfor %}
{% if products_count > 1 %}
{{ tag }}
{% endif %}
{% endfor %}

Shopify: how to get value of forloop.index outside of the loop?

When I attempt to assign the forloop index (or anything else) to a variable in a for-loop and then use it outside (after) the loop, the assigned value is lost. The code below is one of about 20 different approaches I have tried. None of them have worked. I just need to know if x contains y (so the variable can either be boolean or an integer or anything).
{% assign has_y = 0 %}
{% for x in collection %}
{% if x contains y %}
<span style="display: none">{{ has_y | plus: 1 }}</span>
{% endif %}
{% endfor %}
{% if has_y < 1 %}
THIS DOESN'T WORK AS EXPECTED
{% endif %}
I'm confused about Shopify's scoping rules...
The problem is you are outputting {{ has_y | plus: 1 }}, but not assigning anything to has_y inside the for loop.
Try this:
{% assign has_y = 0 %}
{% for x in collections %}
{% if x contains y %}
{% assign has_y = has_y | plus: 1 %}
<span style="display: none">{{ has_y }}</span>
{% endif %}
{% endfor %}
{% if has_y < 1 %}
...
{% endif %}
Or, if you want to use boolean values instead:
{% assign has_y = false %}
{% for x in collections %}
{% if x contains y %}
{% assign has_y = true %}
<span style="display: none">{{ has_y }}</span>
{% endif %}
{% endfor %}
{% if has_y == false %}
...
{% endif %}
Also, you may want to check your for loop {% for x in collection %}. collection is an object. Maybe you meant to iterate over collection.products or collections instead?

Resources