Generating dynamic strings in jinja2 - jinja2

In jinja2, I want to generate strings that enumerate things, different fruits in this example. I have the following template file. Note the line with "Fruit_str not set" is there fore debugging purposes.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
{% set fruit_str = "Fruit_str not set" %}
{% for fruit in fruits %}
{% if loop.first %}
{% set fruit_str = fruit %}
{% elif loop.last %}
{% set fruit_str = fruit_str + " and " + fruit + "." %}
{% else %}
{% set fruit_str = fruit_str + ", " + fruit %}
{% endif %}
{% endfor %}
You have {{fruit_str}}
</body>
</html>
which gets this JSON as input
{ "fruits" : ["apples", "oranges", "bananas", "pears"] }
The expected output would be
You have apples, oranges, bananas and pears.
However, the actual output is
You have Fruit_str not set
This indicates that the loop isn't run at all. ´I have read the jinja2 documentation on loops (http://jinja.pocoo.org/docs/2.10/templates/), but I can't find what is wrong in this example. What is wrong here?

In Jinja2 a for loop have its own namespace; therefore the variables you set within the loop is local to the loop, and once outside the loop, the variable of the same name would revert to the one of the outer scope.
You can use the namespace object to get around this issue:
{% set ns = namespace(fruit_str="Fruit_str not set") %}
{% for fruit in fruits %}
{% if loop.first %}
{% set ns.fruit_str = fruit %}
{% elif loop.last %}
{% set ns.fruit_str = ns.fruit_str + " and " + fruit + "." %}
{% else %}
{% set ns.fruit_str = ns.fruit_str + ", " + fruit %}
{% endif %}
{% endfor %}
You have {{ns.fruit_str}}
Please see the documentation of namespace for details.

Related

How to create or define a dictionary in liquid templates?

According to shopify,
The only possible types of objects that be created are:
String
Number
Boolean
Nil
Array
EmptyDrop
Any workarounds that anyone might be aware of?
Duplicating a workaround from github, (For more visibility)
Emulating / Fake dictionary workaround
Example:
Declaring:
{% assign rgbColors = "red:#FF0000,green:#00FF00,blue:#0000FF" | split: "," %}
{% assign value = "green" %}
Acessing:
{% for color in rgbColors %}
{% assign colorKeyVal = color | split ":" %}
{% assign colorKey = colorKeyVal[0] %}
{% assign colorValue = colorKeyVal[1] %}
{% if colorKey == value %}
<div style="background: {{ colorValue }}"></div>
{% endif %}
{% endfor %}

HTML Script For tags is creating extra space

I've been trying to implement an automatic tag with an app for all of my products on Shopify. It works so that the tag created pulls information from the product desciption. If the description states "Pre-order. Ships in 2-3 weeks." or "Pre-order. Ships January 2021." It will pull whatever appears after the the word "Ships in" or Ships". As a result, the tags appear as such:
preorder**2-3 weeks
preorder**January 2021
The tag is used to insert estimated shipping times throughout the site.
The issue right now is that some of the tags have a space after "preorder** " and we need to make sure there is never a space. Any ideas on how to fix? The Body HTML that it is based off is if it contains "Pre-order. Ships in". Here's our current code:
{% assign body = product.body_html | strip_html | newline_to_br %}
{% assign keywords = "Pre-order. Ships" %}
b{% assign tags = '' %}
{% if body contains keywords %}
{% assign lines = body | split: "<br />" %}
{% for line in lines %}
{% if line contains keywords %}
{% assign tokens = line | split: keywords %}
{% if tokens.size == 2 %}
{% assign inner_tokens = tokens[1] | split: "." %}
{% if inner_tokens.size >= 1 %}
{% assign value = inner_tokens[0] | replace: "in", "" | escape_once | replace: " ", "" %}
{% assign tag = "preorder**" | append:value | replace_first: " ", "" %}
{% assign tags = tags | append: tag | append: ',' %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{{ tags }}
Issue
The issue is happening because "Ships in " has one more space than "Ships ".
Solution
You need to remove the space after the "in".
You can do this easily by modifying your replace operator
Code
replace: "in", "" becomes replace: "in ", "" (note the space).
Your code becomes:
{% assign body = product.body_html | strip_html | newline_to_br %}
{% assign keywords = "Pre-order. Ships" %}
b{% assign tags = '' %}
{% if body contains keywords %}
{% assign lines = body | split: "<br />" %}
{% for line in lines %}
{% if line contains keywords %}
{% assign tokens = line | split: keywords %}
{% if tokens.size == 2 %}
{% assign inner_tokens = tokens[1] | split: "." %}
{% if inner_tokens.size >= 1 %}
{% assign value = inner_tokens[0] | replace: "in ", "" | escape_once | replace: " ", "" %}
{% assign tag = "preorder**" | append:value | replace_first: " ", "" %}
{% assign tags = tags | append: tag | append: ',' %}
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{{ tags }}

Language switching in Hubspot CMS

What is the best way to make custom language switcher to the site.
My problem is that I have a site with two language versions and my custom language switcher is a bit problematic I would say. It doesn't work in all pages for some reason and in my opinion is it coded in very complicated way. In our site the language versions are changed via url. For example mycompany.fi/ is the main language version and the mycompany.fi/en/ is for the english version of the site. Here is the code how I switch language currently by just changing the url. In the code it checks the url and also if the page has translated_content. After that I put the site_language and other_language to links so by pressing the link it changes the language.
{% if absolute_url is string_containing "/en-us/" %}
{% set site_language = 'en' %}
{% set other_language = 'fi' %}
{% if content.translated_content['fi-fi'] %}
{% set other_language_url = '/' + content.translated_content['fi-fi'].slug %}
{% elif content.translated_content['fi'] %}
{% set other_language_url = '/' + content.translated_content['fi'].slug %}
{% else %}
{% set other_language_url = '#' %}
{% endif %}
{% elif absolute_url is string_containing "/en/" %}
{% set site_language = 'en' %}
{% set other_language = 'fi' %}
{% if content.translated_content['fi-fi'] %}
{% set other_language_url = '/' + content.translated_content['fi-fi'].slug %}
{% elif content.translated_content['fi'] %}
{% set other_language_url = '/' + content.translated_content['fi'].slug %}
{% else %}
{% set other_language_url = '#' %}
{% endif %}
{% else %}
{% set site_language = 'fi' %}
{% set other_language = 'en' %}
{% if content.translated_content['en-us'] %}
{% set other_language_url = '/' + content.translated_content['en-us'].slug %}
{% elif content.translated_content['en'] %}
{% set other_language_url = '/' + content.translated_content['en'].slug %}
{% else %}
{% set other_language_url = '#' %}
{% endif %}
{% endif %}
I'm wondering is there any other way to make this happen because for me this seems very complicated solution. I would like to know if there is some global variable or something what defines the current language used in the page.

How can I concatenate a string using Jinja for loop?

I am trying to iteratively concatenate a string to build url params with a 'for' loop, but I believe I am having scoping issues.
The output should be: url_param = "&query_param=hello&query_param=world"
array_of_objects = [{'id':'hello'},{'id':'world'}]
{% set url_param = "" %}
{% set array_of_ids = array_of_objects|map(attribute='id')|list%} // correctly returns [1,2]
{% for id in array_of_ids %}
{% set param = '&query_param='~id %}
{% set url_param = url_param~param %}
{% endfor %}
//url_param is still an empty string
I also tried namespace(), but to no avail:
{% set ns = namespace() %}
{% set ns.output = '' %}
{% set array_of_ids = array_of_objects|map(attribute='id')|list%} // correctly returns [1,2]
{% for id in array_of_ids %}
{% set param = '&industries='~id%}
{% set ns.output = ns.output~param %}
{% endfor %}
//ns.output returns namespace
That is indeed a scope issue. One "hacky" way of dealing with this is using a list that you append to like so:
{% set array_of_objects = [{'id':'hello'},{'id':'world'}] %}
{% set array_of_ids = array_of_objects|map(attribute='id')|list%}
{{ array_of_ids|pprint }} {# output: ['hello', 'world'] #}
{% set ids = [] %} {# Temporary list #}
{% for id in array_of_ids %}
{% set param = '&query_param='~id %}
{% set url_param = url_param~param %}
{{ ids.append(url_param) }}
{% endfor %}
{{ ids|pprint }} {# output: [u'&query_param=hello', u'&query_param=world'] #}
{{ ids|join|pprint }} {# output: "&query_param=hello&query_param=world" #}
The above gets you what you need, but for this specific example I would take a look at using jinja's join filter. It's more declarative and feels a little less hacky.
{% set array_of_objects = [{'id':'hello'},{'id':'world'}] %}
{# set to a variable #}
{% set query_string = "&query_param=" ~ array_of_objects|join("&query_param=", attribute="id") %}
{{ query_string|pprint }}
{# output: u'&query_param=hello&query_param=world' #}
{# or just use it inline #}
{{ "&query_param=" ~ array_of_objects|join("&query_param=", attribute="id") }}
You should change the initialization of your namespace.
Here is an example from the docs that will help you out:
{% set ns = namespace(found=false) %}
{% for item in items %}
{% if item.check_something() %}
{% set ns.found = true %}
{% endif %}
* {{ item.title }}
{% endfor %}
Found item having something: {{ ns.found }}

set value command in jinja2 works in some cases and not in some

The following code displays different values of x in different months. Set command of jinja2 works for flag,x but not for total. Why?
{% set flag = 1 %}
{% set total = 0 %}
{% for date in dates %} //dates is some array
{% if flag == 1 %}
{{x}}
{% set flag= 0 %} // I have used 1+1 also that too works
{% elif "Jul" in date %}
{% set x = x*3 %}
{% if x % 10!=0 %}
{% set x = x - x % 10 %}
{% set total = total + x %}
{% endif %}
{% else %}
{{x}}
{% set total = total + x %}
{% endif %}
{% endfor %}
{{total}}
You have too complex computational logic in the template: it is better to move that logic(or its part) into data-provider component.
For example, in the data-provider component you can compute x for every data and place result into data.x. Then {{dates|sum(attribute='x')}} will display total value.