Template Reference
Complete reference for the mylekha_template_engine template language — syntax, all built-in tags, all built-in filters, and common patterns.
Template Syntax
Variable output
{{ variable }}
{{ object.property }}
{{ list.0 }}
{{ "string literal" }}
{{ 42 }}
{{ true }}
Nested access uses . for both Map keys and List indices:
{{ sale.items.0.name }}
{{ address.zip }}
Filter application
{{ variable | filterName }}
{{ variable | filterName: arg1, arg2 }}
{{ variable | filter1 | filter2: arg }}
Tag blocks
{% tagName args %}
content
{% endtagName %}
Whitespace control
Prefix or suffix with - to trim the adjacent whitespace/newline:
{{- variable }} ← trims whitespace before the output
{{ variable -}} ← trims whitespace after the output
{%- tag %} ← trims whitespace before the tag
{% tag -%} ← trims whitespace after the tag
Object and array literals
{% assign point = { x: 10, y: 20 } %}
{% assign colors = ["red", "green", "blue"] %}
as capture keyword
Any block tag can redirect its rendered output into a variable instead of emitting it:
{% filter upper as heading %}my title{% endfilter %}
<h1>{{ heading }}</h1>
Built-in Tags
assign
Creates a variable scoped to the block. The RHS is a full filter expression.
{% assign subtotal = price | multi: quantity %}
Total: {{ subtotal }}
{% endassign %}
subtotal reverts to its previous value after {% endassign %}. Use {% capture %} if you need the variable outside the block.
capture
Renders the inner content and stores it as a string in the current context scope.
{% capture invoice_header %}
<h1>Invoice #{{ order.number }}</h1>
{% endcapture %}
{{ invoice_header }} ← available throughout the current template
cache
Same as capture, but stores the result at the root context scope — visible across all child contexts including {% include %} partials.
{% cache site_name %}Mylekha POS{% endcache %}
comment
Discards all inner content. Nothing is rendered.
{% comment %}
TODO: add tax calculation
{{ price | multi: 1.1 }}
{% endcomment %}
cycle
On each invocation, advances to the next value in the list (wraps around).
{% for item in items %}
<tr class="{% cycle "odd", "even" %}">
<td>{{ item.name }}</td>
</tr>
{% endfor %}
extends
Inherits from a base template. Must be the first tag in the file. Uses {% block %} to override named sections.
child.html
{% extends "base.html" %}
{% block title %}Order Receipt{% endblock %}
{% block content %}
<p>Order {{ order.number }}</p>
{% endblock %}
base.html
<!DOCTYPE html>
<html>
<head><title>{% block title %}Default Title{% endblock %}</title></head>
<body>{% block content %}{% endblock %}</body>
</html>
The path can also be a variable:
{% extends template_path %}
filter tag
Applies a filter (or chain of filters) to the entire rendered output of the block.
{% filter upper %}
hello world
{% endfilter %}
Output: HELLO WORLD
Chainable:
{% filter capfirst | replace: "old", "new" %}content{% endfilter %}
for
Iterates over a collection. Supports {% else %} or {% empty %} for the empty-collection case.
{% for item in order.items %}
{{ forloop.counter }}. {{ item.name }}
{% else %}
No items.
{% endfor %}
The collection expression supports filters:
{% for item in items | reversed %}
{% for i in count | generateList %}
forloop Variable
Inside a {% for %} block, a forloop map is available:
| Key | Type | Description |
|---|---|---|
counter / index | int | 1-based position |
counter0 / index0 | int | 0-based position |
revcounter / rindex | int | Reverse 1-based position |
revcounter0 / rindex0 | int | Reverse 0-based position |
first | bool | true on the first iteration |
last | bool | true on the last iteration |
length | int | Total number of items |
name | String | "$itemVar-$collectionExpr" |
parentloop | Map | The forloop of the enclosing {% for %} |
{% for item in items %}
{% if forloop.first %}<ul>{% endif %}
<li>{{ forloop.counter }}. {{ item.name }}</li>
{% if forloop.last %}</ul>{% endif %}
{% endfor %}
if / unless
{% if condition %}
...
{% elseif other %}
...
{% else %}
...
{% endif %}
unless negates the initial condition:
{% unless user.active %}Please log in.{% endunless %}
Supported operators:
| Operator | Example |
|---|---|
== | {% if status == "active" %} |
!= | {% if count != 0 %} |
>, <, >=, <= | {% if total > 100 %} |
and | {% if a and b %} |
or | {% if a or b %} |
not | {% if not user.active %} |
contains | {% if list contains item %} |
in | {% if item in list %} |
ifchanged
Renders the inner content only when the output differs from the previous render of this same tag instance. Useful for group headers inside loops.
{% for item in items %}
{% ifchanged %}{{ item.category }}{% endifchanged %}
<p>{{ item.name }}</p>
{% endfor %}
include
Embeds another template, resolved via the Root provided to the parent template's Source.
{% include "partials/header.html" %}
Inject variables into the partial scope:
{% include "partials/item_row.html" with item=current_item label="SKU" %}
only clears all parent variables — only the with assignments are visible in the partial:
{% include "partials/clean.html" with title=page_title only %}
Requires a Root — see Troubleshooting.
load
Activates a named module at render time, registering its filters and tags into the context.
{% load mymodule %}
The module must already be registered before parsing:
context.modules['mymodule'] = MyModule();
block
Defines a named region that child templates can override via {% extends %}.
{% block sidebar %}
Default sidebar content
{% endblock %}
regroup
Groups a List<Map> by the value of a field. Stores the result in a new variable as a list of {grouper, list} maps.
{% regroup cities by country to country_list %}
{% for group in country_list %}
<h3>{{ group.grouper }}</h3>
<ul>
{% for city in group.list %}
<li>{{ city.name }}</li>
{% endfor %}
</ul>
{% endfor %}
Built-in Filters
String
| Filter | Args | Description |
|---|---|---|
upper / upcase | — | Convert to uppercase |
lower / downcase | — | Convert to lowercase |
capfirst / capitalize | — | Capitalize first character |
join | separator? (default ' ') | Join iterable to string |
reversed | — | Reverse a string (char-by-char) or list |
size | — | Length of String/Iterable; 0 otherwise |
isEmpty | — | true if null, empty string, or empty iterable |
isNotEmpty | — | Negation of isEmpty |
default | fallback | Returns input.toString() if non-empty, else first non-empty arg |
default_if_none | fallback | Returns input if non-null, else first non-null arg |
{{ product.name | upper }}
{{ tags | join: ", " }}
{{ description | default: "No description" }}
Numeric Conversion
| Filter | Args | Description |
|---|---|---|
parseInt | — | Convert to int |
parseDouble | — | Convert to double |
parseNum | — | Convert String/num to num |
abs | — | Absolute value |
roundDouble | places? (default 2) | Round to N decimal places |
stringAsFixed | places? (default 0) | Fixed-decimal string representation |
{{ "42.7" | parseInt }} → 42
{{ value | roundDouble: 2 }} → 42.70
{{ price | stringAsFixed: 0 }} → 43
Arithmetic
| Filter | Args | Description |
|---|---|---|
add | n | Adds n to a num; concatenates two lists |
minus | n | Subtracts n from num |
multi | n | Multiplies num by n |
divide | n | Divides num by n |
modulus | n | num % n |
{{ price | multi: quantity }}
{{ total | add: tax }}
{{ index | modulus: 2 }}
Mutating Filters
These filters mutate a Map{value: num} in place and return null (no output). Use them alongside self_add for mutable counters.
| Filter | Args | Description |
|---|---|---|
self_add | n | map['value'] += n |
self_minus | n | map['value'] -= n |
self_multi | n | map['value'] *= n |
self_divide | n | map['value'] /= n |
{% assign counter = { value: 0 } %}
{% for item in items %}
{{ counter | self_add: 1 }} ← no output, mutates counter
{{ counter.value }} ← outputs current count
{% endfor %}
Formatting
| Filter | Args | Description |
|---|---|---|
date / date_format | format? | Format a DateTime or ISO string. Default: 'EEEE, d/MMM/y hh:mm' |
number_format | pattern? | Format as NumberFormat in en_US. Default: "###,###,###.00" |
moneyFormat | places, sign?, isSuffix? | Fixed decimal with optional currency sign prefix (isSuffix=0) or suffix (isSuffix=1) |
{{ order.created_at | date: "d MMM y" }}
{{ total | number_format: "###,###.00" }}
{{ price | moneyFormat: 2, "USD", 0 }} → USD 49.99
{{ price | moneyFormat: 2, "KHR", 1 }} → 49.99KHR
Collection
| Filter | Args | Description |
|---|---|---|
get | key | Returns map[key] |
elementAt | index | Gets item at index from string, list, iterable, or map values |
generateList | — | From integer n, generates [0, 1, ..., n-1] |
mapOf | key? | Maps List<Map> to a list of map[key] values |
whereOf | key, op, value | Filters List<Map> where map[key] op value. Ops: ==, =, >, >=, <, <= |
foldOf | key, op, initial | Folds a numeric field in List<Map>. Ops: +, -, *, /, % |
{{ items | generateList }} → [0, 1, 2, ...]
{{ items | mapOf: "name" }} → ["Alpha", "Beta", ...]
{{ items | whereOf: "active", "==", true }} → filtered list
{{ items | foldOf: "price", "+", 0 }} → sum of prices
Content
| Filter | Args | Description |
|---|---|---|
markdownToHtml | — | Converts Markdown input to HTML (trailing newlines stripped) |
{{ product.description | markdownToHtml }}
Barcode / QR
| Filter | Args | Description |
|---|---|---|
qrcode | width?, height?, type?, fontSize? | Renders a barcode as data:image/png;base64,... |
Defaults: width=300, height=120, type="Code128", fontSize=48
Font sizes: 14 → arial14, 24 → arial24, 48 → arial48
<img src="{{ item.barcode | qrcode: 300, 120, 'Code128', 14 }}" />
<img src="{{ product.code | qrcode: 300, 300, 'qrcode' }}" />
Localization
| Filter | Args | Description |
|---|---|---|
tr | ...args | Translate a key using LocalizationService. Supports positional args and plural forms. |
{{ "welcome" | tr }}
{{ "greeting" | tr: user.name }}
{{ "item_count" | tr: count }}
See Localization for full setup and plural rules.