Vascula v0.1.5

Recipes

Each recipe is a complete, tested example.

#Show something only if it is there

if alone is not enough, because "" and [] are truthy. Compare with blank:

Template
{% if settings.subtitle != blank %}<p class="subtitle">{{ settings.subtitle }}</p>{% endif %}
Data
{"settings": {"subtitle": ""}}
Output

#Fall back to a default

default treats nil, false, "" and empty collections as missing:

Template
<h2>{{ settings.heading | default: "Featured" }}</h2>
Data
{"settings": {"heading": ""}}
Output
<h2>Featured</h2>

#Join a list with commas and "and"

Template
{% for name in settings.names %}{% if forloop.last and forloop.length > 1 %} and {% elsif forloop.index > 1 %}, {% endif %}{{ name }}{% endfor %}
Data
{"settings": {"names": ["Ada", "Grace", "Hedy"]}}
Output
Ada, Grace and Hedy

#Plural words

Template
{{ settings.count }} {% if settings.count == 1 %}item{% else %}items{% endif %}
Data
{"settings": {"count": 1}}
Output
1 item

#A grid with a fixed number of columns

forloop.index0 with modulo finds the first item of each row:

Template
{% for item in (1..5) %}{% assign column = forloop.index0 | modulo: 2 %}{% if column == 0 %}<div class="row">{% endif %}<span>{{ item }}</span>{% if column == 1 or forloop.last %}</div>{% endif %}{% endfor %}
Output
<div class="row"><span>1</span><span>2</span></div><div class="row"><span>3</span><span>4</span></div><div class="row"><span>5</span></div>

#The first few items, then "and more"

Template
{% for tag in settings.tags limit: 2 %}{{ tag }} {% endfor %}{% if settings.tags.size > 2 %}and {{ settings.tags.size | minus: 2 }} more{% endif %}
Data
{"settings": {"tags": ["new", "sale", "tea", "gift"]}}
Output
new sale and 2 more

#Filter, sort and pick

Template
{% assign cheapest = settings.products | where: "available" | sort: "price" | first %}Cheapest available: {{ cheapest.title }}
Data
{"settings": {"products": [
  {"title": "Pot", "price": 30, "available": true},
  {"title": "Cup", "price": 8, "available": false},
  {"title": "Jug", "price": 12, "available": true}
]}}
Output
Cheapest available: Jug

#Put data in an attribute or a script

json is safe in both places:

Template
<div data-config="{{ settings.config | json }}"></div><script>const config = {{ settings.config | json }};</script>
Data
{"settings": {"config": {"autoplay": true, "delay": 3}}}
Output
<div data-config="{&#34;autoplay&#34;:true,&#34;delay&#34;:3}"></div><script>const config = {"autoplay":true,"delay":3};</script>

#A short excerpt from rich text

Template
{{ settings.body | strip_html | truncatewords: 5 }}
Data
{"settings": {"body": "<p>Our <b>first flush</b> Darjeeling has arrived this week.</p>"}}
Output
Our first flush Darjeeling has...

#Mark the current item

Template
{% for link in settings.links %}<a href="{{ link.url }}"{% if link.url == settings.current %} aria-current="page"{% endif %}>{{ link.title }}</a>{% endfor %}
Data
{"settings": {"current": "/teas", "links": [{"title": "Home", "url": "/"}, {"title": "Teas", "url": "/teas"}]}}
Output
<a href="/">Home</a><a href="/teas" aria-current="page">Teas</a>

#Group items under headings

There is no group_by; sort by the group, then print a heading whenever it changes:

Template
{% assign last = nil %}{% for t in settings.teas | sort: "kind" %}{% if t.kind != last %}<h3>{{ t.kind }}</h3>{% assign last = t.kind %}{% endif %}<p>{{ t.name }}</p>{% endfor %}
Data
{"settings": {"teas": [{"name": "Sencha", "kind": "green"}, {"name": "Assam", "kind": "black"}, {"name": "Matcha", "kind": "green"}]}}
Error
expected "%}"

A loop's collection cannot take filters directly. Assign the sorted list first:

Template
{% assign sorted = settings.teas | sort: "kind" %}{% assign last = nil %}{% for t in sorted %}{% if t.kind != last %}<h3>{{ t.kind }}</h3>{% assign last = t.kind %}{% endif %}<p>{{ t.name }}</p>{% endfor %}
Data
{"settings": {"teas": [{"name": "Sencha", "kind": "green"}, {"name": "Assam", "kind": "black"}, {"name": "Matcha", "kind": "green"}]}}
Output
<h3>black</h3><p>Assam</p><h3>green</h3><p>Sencha</p><p>Matcha</p>