HuBL: Using for loop variable outside for loop - for-loop

I have created a template partial file where I'm defining all variables in that file. I then want to reference a variable in custom modules. However, the for loop loop closes in the partial file, so when I reference it in the HTML of a custom module, it'll be out of scope.
Is there any way to use for loop variables outside the loop?
Demo:
Here is my template partial file:
<!--
templateType: "global_partial"
isAvailableForNewContent: false
-->
{% set table = hubdb_table_rows(table_id_here) %}
{% for row in table %}
{% set firstname = row.first_name %}
{% endfor %}
And here is how I'm calling the variable in my markup:
<div class="hero">
{% include "/template_partial_file.html" %}
<h1>{{ firstname }}</h1>
</div>

You can't access a variable set inside a for loop outside of said loop.
You can set up an array outside of a loop and push information from within the loop to it, e.g.
{% set names = [] %}
{% for row in table %}
{% do names.append(row.first_name) %}
{% endfor %}

Related

Jinja2 Templating: Conditionally forming a set from Ansible Variables

I am trying to generate some config using Jinja2 templating and Ansible variables. The framework under which I am currently working does not allow me to perform the following operation in Ansible and thus I was hoping to achieve the same results in Jinja2.
My Ansible variables are as follows:
---
Top:
inner:
type: type1
other_random_variable:
- random: 1
inner2:
type: type2
inner3:
type: type1
The above structure works well when I am iterating over a loop and forming a configuration file as follows:
{% if Top is defined %}
{% for inner_vars in Top %}
# perform substitution here
{% endfor %}
{% endif %}
What I would like to do is to form a set of types such that I can generate another configuration for each unique type.
Is there any way for me to iterate through Top and add an item to a set?
I think I have a solution that could work:
{% set types = [] %}
{% if Top is defined%}
{% for inner_var in Top %}
{% if types.append(Top[inner_var].type) %}{% endif %}
{% endfor %}
{% endif %}
{{ types|unique }}

Shopify Liquid How do I use for-loop index variables in an assign or capture tag?

I am just getting into some liquid template coding on a Shopify site. How do I use the for-loop index/variable when defining variable names in assign or capture tags? ie: I'm trying to condense the code to create multiple link menus from sequentially numbered settings, where [i] would be a number between 2 and 4 in the settings key.
What is the proper syntax to insert that number into
a) a tag like an if statement or assign.
b) interpolated text like in the h3 element below.
c) a nested/bracketed key statement (sorry if that's not what its called, i'm still learning), like in the second for statement.
{% for i in (2..4) %}
{% if settings.footer_quicklinks_enable[i] %}
<div class="grid-item medium--one-half large--three-twelfths">
<h3>{{ 'layout.footer.quick_links{{i}}' | t }}</h3>
<ul>
{% for link in linklists[settings.footer_quicklinks_linklist[i]].links %}
<li>{{ link.title }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endfor %}
You need to use the square bracket notation instead of dot notation.
Create a string containing the variable name (with assign or capture), then use square bracket notation to access the setting with that name.
For example:
{% capture var %}footer_quicklinks_enable_{{i}}{% endcapture %}
{% if settings[var] %}
Also see this similar answer.

Store Twig loop for multiple reuse

I have two arrays that I loop through, but they are nested.
Reduced to the minimum, it looks like this:
{% for item in items %}
<label>{{ item.name }}</label>
<select>
{% for attribute in attributes %}
<option>{{ attribute.name }}</option>
{% endfor %}
</select>
{% endfor %}
The problem is the size of the arrays. There are about 1,100 items and 400 attributes. As one can guess, this is slow. Very slow.
Is it possible to "store" the inner loop, and just reuse the generated/rendered block?
Move the inner loop outside of the outer loop and then store the value generated in a Twig variable. Then run the outer loop supplying the variable between the <select> tags. This way you only generate the inner loop once.
{% set options = '' %}
{% for attribute in attributes %}
{% set options = options ~ '<option>' ~ attribute.name ~ '</option>' %}
{% endfor %}
{% for item in items %}
<label>{{ item.name }}</label>
<select>
{{ options }}
</select>
{% endfor %}

Selectively rendering Liquid templates?

Is there a possibility to selectively render specific tags in a liquid template and leave the rest for a second render?
I have pages containing snippets(includes) and variables. The snippets are stored in the database and expensive to render. The variables are available only at runtime (via the URL request in the scenario of a landing page). I want to cache the page content with the snippets rendered but with all the rest of the liquid markup untouched.
So, If I have
{% snippet header %}
{% if vars.first_name %}
Welcome, {{ vars.first_name }}
{% endif %}
{% snippet footer %}
I would want the cached page content to be:
The header content
{% if vars.first_name %}
Welcome, {{ vars.first_name }}
{% endif %}
The footer content
At runtime this would be picked up from the memcached store and rendered:
The header content
Welcome, John
The footer content
Any idea on how to achieve this?
Update: Here's what I have in place already:
(It works, but I am looking for a cleaner, ideally liquid-only-based solution.)
A "vars" tag which produces a variable with the given name:
{% vars first_name %} #=> {{ vars.first_name }}
And, I use modified liquid markup for everything I don't want rendered the first time:
{* if vars.first_name *}
So, currently the initial page looks like this:
{% snippet header %}
{* if vars.first_name *}
Welcome, {% vars first_name %}
{* endif *}
{% snippet footer %}
Which gets rendered once and cached as:
The header content
{* if vars.first_name *}
Welcome, {{ vars.first_name }}
{* endif *}
The footer content
Then at runtime I retrieve the cached version and replace {* with {% etc. to get
The header content
{% if vars.first_name %}
Welcome, {{ vars.first_name }}
{% endif %}
The footer content
Which I render with liquid again to get to the desired outcome.
This does the job but is not pure liquid and I was wondering if there is a cleaner solution.
Is there?
{% snippet header %}
{% raw %}{% if vars.first_name %}
Welcome, {{ vars.first_name }}
{% endif %}{% endraw %}
{% snippet footer %}
This should get you the rendering that you want to cache, and then if you re-render it through Liquid I would think it would process the runtime variable.

django-registration: Cannot translate email and subject

Any .html template for django-registration module works fine with {% blocktrans %} and {% trans %} template blocks. With {% load i18n %} in place, of course.
But I cannot make use of i18n tags in activation_email.txt and activation_email_subject.txt templates. Strings marked for translation just don't appear in .po file after makemessages.
Also, when wrapping a text with {% blocktrans %}{% endblocktrans %}, all variables such as {{ site.domain }} and {{ site.name }} are not processed.
Can you suggest what I am doing wrong?
That was my bad, I just improperly used makemessages. By default it processes only .html files.
In my case
django-admin.py makemessages -a -e html,txt
does all the work.
As for variables, {% blocktrans %}{% endblocktrans %} cannot process variables inside object, so we have to retrieve them before translation:
{% blocktrans with site.name as site_name and site.domain as site_domain %}
Good examples of templates for django-registration are given here.

Resources