Guidance on readability, variable scope, and keeping templates maintainable.
Var sets the value on the current scope
{{ test1 }}
{%- var test1 = 'out1' -%}
,{{ test1 }}
{%- if true -%}
,{{ test1 }}
{%- var test1 = 'out2' -%}
,{{ test1 }}
{%- endif -%}
,{{ test1 }},out1,out1,out2,out1
The first time the variable is output it has not been set yet so it does not output anything. The second time it has been set to out1. The third time it has not been set on the current scope but it is still set on the parent scope so it outputs out1 again. The next time it has been set to out2 on the current scope so that is output instead. When the current scope ends (with the endif method) the variable is still set on the parent scope so it outputs out1 again.
Set changes the value on the closest scope
{%- var test2 = 'out1' -%}
{{ test2 }}
{%- if true -%}
{%- var test2 = 'out2' -%}
,{{ test2 }}
{%- set test2 = 'out3' -%}
,{{ test2 }}
{%- if true -%}
{%- set test2 = 'out4' -%}
,{{ test2 }}
{%- endif -%}
,{{ test2 }}
{%- endif -%}
,{{ test2 }}out1,out2,out3,out4,out4,out1
The first time the variable is output it has been set to out1 on the root scope. The second time it has been set to out2 on the current scope using the var method so it is still unchanged on the root scope. The third time it has been updated to out3 on the nearest scope that it is defined for, which also happens to be the current scope. The fourth time it has been updated to out4 on the parent scope, which is the nearest scope that it is defined for. When the current scope ends and returns to the parent scope, the updated value is still present. When that scope ends and it returns to the root scope, the variable returns to the value that was set on the root scope.
Assign sets the value on the root scope and removes the variable from all other scope
{{ test3 }}
{%- if true -%}
{%- var test3 = 'out1' -%}
,{{ test3 }}
{%- if true -%}
{%- assign test3 = 'out2' -%}
,{{ test3 }}
{%- endif -%}
,{{ test3 }}
{%- endif -%}
,{{ test3 }},out1,out2,out2,out2
The first time the variable is output it has not been set yet so it does not output anything. The second time it has been set to out1 on the current scope (not the root scope). The third time it has been set to out2 on the root scope by the assign method, which also clears it from all other scopes. Then the fourth and fifth times it outputs out2 from the root scope.
Store a value in a variable using a filter
{%- var title = entity.name | default: 'Untitled' -%}
<p>{{ title }}</p>Stores the result of the expression in a variable for reuse.
Create an empty variable, then update it as needed using the set method
{%- var tag = '' -%}
{%- if entity.tags is_list and entity.tags.is_valid -%}
{%- set tag=entity.tags | first -%}
{%- endif -%}
{%- datastore_items datastore:"products" tag:tag -%}Conditionally stores the first tag in the tags list in the tag variable. Then uses the tag variable to filter the datastore items. Using var and set in this way prevents the tag variable from being overwritten in the parent scope.
Duplicate a variable, then update it as needed to preserve the original value
{%- var tag = tag -%}
{%- if entity.tags is_list and entity.tags.is_valid -%}
{%- set tag=entity.tags | first -%}
{%- endif -%}
{%- datastore_items datastore:"products" tag:tag -%}By creating a new variable on the current scope from an existing variable on the parent scope, we can update the variable using the set method without affecting the parent scope. Note that even if the variable is not set on the parent scope the current scope will still define the variable with a null value.
Use assign to force the variable to be accessible from the root scope
{%- if entity.tags is_list and entity.tags.is_valid -%}
{%- assign num_tags = entity.tags | size -%}
{%- endif -%}
There are {{num_tags | default:0}} tags on this entityThe assign method creates a root-scope variable, so it is accessible from the root scope and all child scopes.