Variables, data and scope
#Where names come from
When a template reads a name such as product in {{ product.title }}, Vascula looks it up in this order:
- Variables the template created with
assignorcapture, and loop variables inside their loop. - **
settings**: the template's own parameters. Always readable. - Globals: names the application makes readable everywhere, such as
themein a theme system. - Data roots the application allowed for this template.
Anything else is an error, so a typo cannot quietly print nothing:
{{ produt.title }}{"data": {"product": {"title": "Cup"}}, "allow": ["product"]}undeclared name "produt"The application decides which data roots a template may read (see Embedding). A root in the data but not allowed is just as unreadable as a name that does not exist.
#Reading fields
Use a dot for a field and brackets for a key or position.
{{ product.title }} / {{ product["title"] }} / {{ product.tags[0] }} / {{ product.tags[-1] }} / {{ product.tags[settings.i] }}{"data": {"product": {"title": "Cup", "tags": ["new", "sale", "blue"]}}, "allow": ["product"], "settings": {"i": 1}}Cup / Cup / new / blue / saleA negative position counts from the end. A missing field or an out-of-range position is nil, which prints nothing; only the first name of a path must exist.
[{{ product.missing }}] [{{ product.missing.deeper }}] [{{ product.tags[9] }}]{"data": {"product": {"tags": ["new"]}}, "allow": ["product"]}[] [] []#size, first and last
Every array, map and string has three built-in properties. A field of the same name in a map wins.
| Property | Array | Map | String |
|---|---|---|---|
.size | number of items | number of keys | number of characters |
.first | first item | nil | nil |
.last | last item | nil | nil |
{{ product.tags.size }} {{ product.tags.first }} {{ product.tags.last }} {{ product.title.size }}{"data": {"product": {"title": "Café", "tags": ["new", "sale"]}}, "allow": ["product"]}2 new sale 4.size of something missing is 0, so {% if product.images.size > 0 %} is safe even when images is absent.
#assign
assign stores the value of an expression, filters included.
{% assign title = product.title | upcase %}{% assign count = product.tags.size %}{{ title }} has {{ count }} tags.{"data": {"product": {"title": "Cup", "tags": ["new", "sale"]}}, "allow": ["product"]}CUP has 2 tags.#capture
capture renders its body and stores the result as a string. The stored string is ordinary text: printing it escapes it again.
{% capture label %}{{ product.title }} ({{ product.tags.size }}){% endcapture %}<span title="{{ label }}">{{ label }}</span>{"data": {"product": {"title": "Cup", "tags": ["new"]}}, "allow": ["product"]}<span title="Cup (1)">Cup (1)</span>For reusable markup, prefer [render](/composition) to capture.
#Scope
A template has one set of variables. A value assigned or captured anywhere, including inside if, case, for or capture, is available after that block ends.
{% for n in (1..5) %}{% if n > 2 %}{% assign first_big = first_big | default: n %}{% endif %}{% endfor %}{{ first_big }}undeclared name "first_big"Reading a variable before anything has assigned it is still an error, as the example shows: the first default reads first_big before it exists. Assign a starting value first:
{% assign first_big = nil %}{% for n in (1..5) %}{% if n > 2 %}{% assign first_big = first_big | default: n %}{% endif %}{% endfor %}{{ first_big }}3Three exceptions keep things predictable:
- Loop variables belong to their loop. After
{% endfor %}the name is gone, and assigning to it inside the loop changes only the current item. - **
forloop** exists only inside a loop. - A child template rendered with
{% render %}has its own variables. It cannot see the caller's, and its assignments do not leak back. See Composition.
{% assign total = 0 %}{% for price in settings.prices %}{% assign total = total | plus: price %}{% endfor %}Total: {{ total }}{"settings": {"prices": [3, 4, 5]}}Total: 12#Reserved names
settings and forloop cannot be assigned, captured, used as a loop variable or passed to a child. Applications reserve their own globals the same way, so a template cannot hide them.
{% assign settings = "x" %}assignment name "settings" is reservedNext: Expressions and operators.