What is the hyphen for, apart from trimming whitespace? - twig

I understand using
{{- remove leading spaces -}}
for trimming whitespace, but in the example below, and that I've seen loads, what is the point?
{%- if true -%}
or
{%- endfor %}
What purpose does it serve in a control statement? I can find nothing in the documentation about this specifically, but if there is something, please, point me in the right direction!

It applies to the full block,
{% for i in 1..10 %}
{{ i }}
{% endfor %}
{%- for i in 1..10 -%}
{{ i }}
{%- endfor -%}
output
1
2
3
4
5
6
7
8
9
10
12345678910
demo

Related

Shopware6 Social Shopping: Google Shopping Feed: Varianten template <g:size>

H
I added structured data values such as:
<g:color>, <g:material>.
{% for properties in product.properties %}
{% if properties.group.name == "Kolor" %}
<g:color>{{ properties.name }}</g:color>
{% endif %}
{% endfor %}
{% for properties in product.properties %}
{% if properties.group.name == "Skład" %}
<g:material>{{ properties.name }}</g:material>
{% endif %}
{% endfor %}
, but I don't know how I can add <g:size> from product variants?
First of all you have to enable "Export variants as discrete products" in the settings of your product comparison sales channel.
There are two separate association to properties:
product.properties for general properties, not necessarily variant specific
product.options which define the combination of properties for a specific variant.
You may want to iterate product.options instead.
0
I tried this:
{%- if product.variation -%}
{%- for variation in product.variation -%}
{%- if variation.group == 'Kolor' -%}
<g:color>{{ variation.option }}</g:color>
{%- endif -%}
{%- if variation.group == 'Wymiary' -%}
<g:size>{{ variation.option }}</g:size>
{%- endif -%}
{%- endfor -%}
{%- endif -%}
But it doesn't work
First of all you have to enable "Export variants as discrete products" in the settings of your product comparison sales channel.
There are two separate association to properties:
product.properties for general properties, not necessarily variant specific
product.options which define the combination of properties for a specific variant.
You may want to iterate product.options instead.

Macro causing white space in Jinja2?

I was expecting that this macro
{% macro join_them(first) -%}
{% set data = [] %}
{% if first not in ["", None, "None"] %}
{{ data.append(first) }}
{% endif %}
{% for arg in varargs %}
{% if arg not in ["", None, "None"] %}
{{ data.append(arg) }}
{% endif %}
{% endfor %}
{{' '.join(data)}}
{%- endmacro %}
Would render this
<td> * {{ join_them(docs["Author"]["Name"].title, docs["Author"]["Name"].first, docs["Author"]["Name"].middle, docs["Author"]["Name"].last, docs["Author"]["Name"].suffix) }}</td>
As this line, without the use of the macro, does
<td> * {{ ' '.join([docs["Author"]["Name"].title, docs["Author"]["Name"].first, docs["Author"]["Name"].middle, docs["Author"]["Name"].last, docs["Author"]["Name"].suffix])}}</td>
The non macro version is working fine, and outputing the name information correctly, but the Macro version is putting out white space and some "None" strings.
e.g.
<td> *
None
None
Computer Community
*</td>
<td>Computer Community </td>
Anyone have any suggestions on what I'm missing here? Or a better way to handle this? I don't typically use macros in Jinja2, but this seemed a perfect opportunity to do so.
The macro is indeed causing those blank lines, and you had the right approach already, you need to use whitespace control to remove them. You only placed them at the beginning and the end of your macro but note that:
You can also strip whitespace in templates by hand. If you add a minus sign (-) to the start or end of a block, a comment, or a variable expression, the whitespaces before or after that block will be removed
Source: https://jinja.palletsprojects.com/en/3.1.x/templates/#whitespace-control, emphasis, mine
So, for each and every single block in your macro, you have to repeat this operation. To be extra sure, what you could do here is to frame any block in the macro with the minus sign.
Then for the None appearing, this is because you are using an expression delimiter {{ ... }} to append to your array, when you should use a statement delimiter {% ... %} and a do, as pointed in the documentation
If the expression-statement extension is loaded, a tag called do is available that works exactly like the regular variable expression ({{ ... }}); except it doesn’t print anything. This can be used to modify lists
{% do navigation.append('a string') %}
Source: https://jinja.palletsprojects.com/en/3.1.x/templates/#expression-statement
So, your macro should end up looking like:
{%- macro join_them() -%}
{%- set data = [] -%}
{%- for arg in varargs if arg not in ["", None, "None"] -%}
{%- do data.append(arg) -%}
{%- endfor -%}
{{- ' '.join(data) -}}
{%- endmacro -%}
Note that, if you don't have the expression-statement extension loaded, you could also replace the {% do ... %} blocks by this inline-if 'hack':
{{- '' if data.append(arg) -}}

Shopify: Adding a Custom Content Block to Collection Pages inside the Product Grid

I'm attempting to create a custom content block that can be placed anywhere in the product grid in collection pages.
I can get a custom content block to render inside the product grid on collection pages, and I've linked up a range slider in the block's schema to choose the grid position number. That was easy enough.
I'm running into a problem with pagination. Say there's 9 products per page (3 products per row, 3 rows total) and 12 products in the store. Without the custom content block, 9 products display per page, but turning the custom content block on will add a tenth block to the first page.
Problem #1: How to update the product grid so that when a custom content block is added into the grid, the 9th product flows on to the second page? And so on for each subsequent page in the pagination?
The range slider for the custom content block has a max of 9, because if it goes above that number then the custom content block disappears, the reason being, pagination breaks the products into 9 products per page. Page/2 in the pagination has only 9 products, and so on for each page.
Problem #2: How to get the custom content block to appear on the 2nd or 3rd (and so on) pages in the pagination?
A simplified version of my collection-template.liquid file:
{%- liquid
assign per_row = 3
assign paginate_by = per_row | times: 3
for block in section.blocks
case block.type
when 'product_grid'
assign per_row = block.settings.per_row
assign paginate_by = per_row | times: block.settings.rows_per_page
endcase
endfor
-%}
{%- paginate collection.products by paginate_by -%}
{%- for block in section.blocks -%}
<div {{ block.shopify_attributes }}>
{%- case block.type -%}
{%- when 'product_grid' -%}
{% assign product_counter = 0 %}
{%- for product in collection.products -%}
{%- assign product_counter = product_counter | plus: 1 -%}
{% if product_counter == grid_placement %}
{%- render 'product-grid-promo', grid_item_width: grid_item_width, per_row: per_row -%}
{%- endif -%}
{%- render 'product-grid-item', product: product, grid_item_width: grid_item_width, per_row: per_row -%}
{%- endfor -%}
</div>
{%- if paginate.pages > 1 -%}
{%- render 'pagination', paginate: paginate -%}
{%- endif -%}
{%- endcase -%}
</div>
{%- endfor -%}
{%- endpaginate -%}
I want this to be flexible enough, that my client (the merchant) could add several custom content blocks into the product grid, and the grid would adjust accordingly. I also want the ability to add custom content blocks to each page in the pagination.
Any ideas?
I figured out how to do this, so I'm answering my own question. Hopefully this helps someone else.
First we count the number of content blocks (promo blocks) in the loop:
{%- liquid
assign promo_block_counter = 0
for block in section.blocks
case block.type
when 'product_grid_promo'
assign promo_block_counter = promo_block_counter | plus: 1
when 'product_grid'
assign per_row = block.settings.per_row
assign paginate_by = per_row | times: block.settings.rows_per_page
endcase
endfor
-%}
Then we assign paginate_by to a variable, and run the variable through a switch statement where the case is the number of promo blocks in the loop. If the number of promo blocks is 1, we subtract 1 from paginate_by, and so on up to 3 (in this instance I didn't want the merchant to be able to add more than 3 promo blocks into the grid):
{%- liquid
assign number_of_grid_items = paginate_by
case promo_block_counter
when 1
assign number_of_grid_items = paginate_by | minus: 1
when 2
assign number_of_grid_items = paginate_by | minus: 2
when 3
assign number_of_grid_items = paginate_by | minus: 3
endcase
-%}
We update the paginate wrapper with the now correct number of items in the grid, which will adjust according to how many promo blocks have been added:
{%- paginate collection.products by number_of_grid_items -%}
A few notes about the code below. I used to have the promo block code in its own snippet liquid file, but it appears that when you have a block nested inside a block, as is the case below (promo block nested inside product grid block), I wasn't able to figure out a method for counting the promo blocks. The only way I could get it to work, was to bring the snippet code directly into the collection template file. One thing I am not totally sure of, is the forloop inside the forloop causing any performance issues? Is there a better way to structure this?
{%- paginate collection.products by number_of_grid_items -%}
{%- for block in section.blocks -%}
<div {{ block.shopify_attributes }}>
{%- case block.type -%}
{%- when 'product_grid' -%}
{%- assign product_counter = 0 -%}
{%- for product in collection.products -%}
{%- assign product_counter = product_counter | plus: 1 -%}
{%- for block in section.blocks -%}
{%- case block.type -%}
{%- when 'product_grid_promo' -%}
{%- if product_counter == block.settings.grid_placement -%}
<div class="grid__item grid-product {{ grid_item_width }}" data-block-id="{{ block.id }}" {{ block.shopify_attributes }}>
{%- comment -%} promo block content goes here {%- endcomment -%}
</div>
{%- endif -%}
{%- endcase -%}
{%- endfor -%}
{%- render 'product-grid-item', product: product, grid_item_width: grid_item_width, per_row: per_row -%}
{%- else -%}
<div class="grid__item">
<p>{{ 'collections.general.no_matches' | t }}</p>
</div>
{%- endfor -%}
</div>
{%- if paginate.pages > 1 -%}
{%- render 'pagination', paginate: paginate -%}
{%- endif -%}
{%- endcase -%}
</div>
{%- endfor -%}
{%- endpaginate -%}

How to create array using Tera in Rust?

I am stuck in a simple problem but not able to figure it. I am not sure if this is the right place to ask the question on a package in Rust.
Most of the time, in the template, we will want to transform our data. For example, I wanted to concat n arrays in one line. I can use ~ operator only if I know the number of arrays.
Below is the requirement I am looking for,
{% macro generate_table(table) %}
{% for rows in 0..table.length %}
{{ table[table.col_header[0]][row] ~ " || " ~ [table.col_header[1]][row] }}
{% endfor %}
{% endmacro input %}
I want to do.
{% macro generate_table(table) %}
{% for rows in 0..table.rlength %}
{% for cols in 0..table.clength %}
{{ arr.insert(table[table.col_header[cols]][row]) }}
{% endfor %}
{{ arr | join(sep=" || ") }}
{% endfor %}
{% endmacro input %}
I figured it out. Using concat(with="")
{% macro generate_table(table) -%}
{% for row in [0,1,2] -%}
{% set_global row_val = [] -%}
{% for cols in [0,1,2] -%}
{% set_global row_val = row_val | concat(with= table.col_values[table.col_header[cols]][row]) -%}
{% endfor -%}
{{ row_val | join(sep=" ") }}
{% endfor -%}
{% endmacro generate_table -%}

Breaking Nunjucks Loop

I've racked my brain in all directions but still no solution. Maybe someone has some advice?
I have the following block in an ExpressJS app using Nunjucks as the templating engine.
{%- for course in courses -%}
{%- for rcourse in report.courses -%}
{%- if rcourse.id == course.id -%}
<li {{ 'class="column-pre"' if loop.last else ''}}><span>Cool!</span></li>
{%- else -%}
<li></li>
{%- endif -%}
{%- endfor -%}
{%- endfor -%}
My problem: I need to break the inner report.courses loop when the conditional proves truth-y. Basically, the moment that I print the non-empty <li> line, I need to jump to the next iteration of the courses loop.
I know that Nunjucks does't have a break for loops like Jinja2, Nunjucks' variables are scoped (so I can't set a sentinel-like variable that gets modified in the if/else statement), nor can I append to an array so that I can use array.length as a way to determine if I should print the <li></li> line.
Maybe someone has a clever solution?
I think the sentinel-like variable would not be affected by scoping:
{% for course in courses %}
{% set searchrcourse = true %}
{% for rcourse in report.courses %}
{% if searchrcourse %}
{% if rcourse.id == course.id %}
<li {{ 'class="column-pre"' if loop.last else ''}}><span>Cool!</span></li>
{% set searchrcourse = false %}
{% else %}
<li></li>
{% endif %}
{% endif %}
{% endfor %}
{% endfor %}

Resources