Templating Reference
Modulo's templating language comes with a standard library of filters and
template-tags, modeled to be similar to the behavior of Django's templating
language library. Although in general Django's has more features, some Django
templates might work as Modulo templates with a few tweaks. In fact, there's
such large overlap that some of this very documentation was copied and
arranged just like Django's own, to be instantly more familiar to
developers. Thanks, Django!
#
Built-in Template Tags
comment
Used specifically for commenting out blocks of Templating code. Ignores
everything between {% comment %}
and {% endcomment
%}
. An optional note may be inserted in the first tag. This can be
useful to document why the code was commented out or disabled. You should avoid
nesting comment tags.
debugger
Inserts a "debugger" statement at that location in JavaScript. This allows
for detailed debugging of the generated code.
In the following demo, you can uncomment the "debugger" statement to
practice "stepping through" the for loop, examining the CTX.user
variable for each iteration in the Developer Tools debug panel.
zer0c00l
ac1dburn
the_phantom_phr34k
include
Include another Templater, specified by it's name. It's useful for
breaking up large and complicated templates into parts that are loaded
independently.
for
For-loops are for repeating the same HTML code multiple times. It will
repeat a block of templating code for every item in a specified array or
object. It's useful for displaying search results, blog articles, chat
messages, and much more: Any time you have unknown quantities or
plural amounts of data that you want to template on the screen.
At it's core, {% for %}
will loop over each item in an array,
making the item available in a specified template variable. This is often done
to objects in an array stored in state, such as data that came back from an
API. Examine the following example:
- Devante Adams
- Steph Curry
- Megan Rapinoe
Note that your for loops should conventionally follow the pattern of
{% for SINGULAR_THING in PLURAL_THINGS %}
. For 3 examples of this:
{% for item in state.item_data %}
,
{% for user in users %}
, or
{% for city in cities %}
.
Within the repeated code of the for
loop, then the "singular thing" (the variable defined by the for loop) will be
available as a template variable. Typically, the "plural variable" will be an
Array or Object defined from the State
or Props
CParts. Also, it's worth noting that you can loop over an array in reverse by
using the |reversed
filter, e.g.
{% for obj in list|reversed %}
.
If you need to loop over an object, you can unpack each key / value
pair into individual variables. For example, if your state contains an array of
(x, y) coordinates called state.data
, you could use the following to output the list
of points:
- Devante Adams: Green
- Steph Curry: Golden
- Megan Rapinoe: Blue
empty
Often you will want to include a default, empty, or "404 message" if nothing
is in the array of a for
tag. To avoid cluttering your loops with
extra if-statements, the for tag can take an optional {% empty %}
clause, which functions identically to an if-statement that executes only if
the given data is empty or could not be found (e.g. if 0 iterations of the loop
were run). Observe the following example:
if
The if-statement allows a block of HTML code to be conditionally
included. It allows "branching" in your templates: That is, one or more
optional "branches" or blocks of HTML templating code that will be included
only if a certain condition is satisfied.
The simplest behavior of the {% if %}
tag is to evaluate a variable,
and if that variable both exists and is a "truthy" value according to JavaScript
(see MDN "Truthy"),
the specified block will be included. By default, if
will include
(or not) all code until it encounters an {% endif %}
tag to close.
However, it also supports using an {% else %}
tag before the
{% endif %}
tag, which can provide alternative code.
Examine the following example, and observe what happens as you change state
variables from true
to false
, or vice-versa:
Hello testing template world!
elif
Like with the else
tag, the if
tag may also
optionally proceed {% elif %}
tags, which is a shortened version
of the word "else" and "if", combined into a single, made-up word "elif". The
behavior of the {% elif %}
is very similar to the "if": It requires
specifying condition which will be evaluated, and it will only include the
specified code block if that condition evaluates to be true. Unlike the
{% else %}
tag, a single {% if %}
can has as many
{% elif %}
tags as you'd like, and it is guaranteed that it will
only execute one of those. In other words, only one {% if %}
,
{% elif %}
, or {% else %}
code block will be
executed, and there's never a risk that two "elifs" could get executed in the
same "chain".
For an example of this, examine the following more complicated example:
Benched exists. Total benched: 1
In the above, if state.athletes
exists, the number of athletes will be
displayed by the {{ state.athletes|length }}
filtered template variable.
Operators
Within the if tag, you can use a variety of "operators". Operators behave
similarly to JavaScript operators: They allow comparisons of template variables
with other template variables and/or hardcoded values. There are about a dozen
built-in operators. As with template-tags, it is also possible to configure
your own custom operators.
Built-in operators: ==, >, <, >=, <=, !=, not in, is not,
is, in, not, gt, lt
==
, is
(operator)
Check for equality. Note that this will be a "strict" comparison, equivalent
to JavaScript's triple-equals ===
operator. The syntax variants
==
and is
are aliases of each other.
This appears if variable somevar equals the string "x"
!=
, is not
(operator)
Check for inequality. Note that this will be a "strict" comparison, equivalent to
JavaScript's triple-not-equals !==
operator. The syntax variants
!==
and is not
are aliases of each other.
This appears if variable state.somevar does not equal the string "x".
not
(operator)
You can use "not" to invert or negate an "if" tag:
lt
, <
, <=
(operators)
Less than. Has a few variants: lt
and <
will
check to see if a variable is strictly less than a given variable or value,
while <=
will be "less than or equal to" and thus also allow it
if its equal. Example:
This appears if variable somevar is less than 100.
gt
, >
, >=
(operators)
Greater than. Has a few variants: gt
and >
will
check to see if a variable is strictly greater than a given variable or value,
while >=
will be "greater than or equal to" and thus also allow
it if its equal. Example:
This appears if variable somevar is greater than 100.
in
, not in
(operators)
Contained within. Unlike the built-in JavaScript operator "in" which is only
supported by Objects, this operator supports Strings, Arrays, and Objects as
the "container" types being checked. In all cases, it will test whether the
given value is within the given container. The not in
operator
does what you might expect: The exact opposite of the in
operator.
The following are some examples of how the in
operator will be
interpreted in different contexts:
This appears since "B.C.E." is a substring of "3rd Century B.C.E. (Macedonia)"
Note that in these examples, some use hardcoded strings in places (e.g.
"B.C.E"
), while others use variables in the same place (e.g.
state.word
). As with every if-statement operator, you can either
hardcoded values and variables are interchangeable.
Filters
You can also use filters in combination with operators within an if tag. For
example:
There are more than 2 athletes!
#
Built-in Template Filters
add
Adds the argument to the value. Example:
allow
Input validation? It's not recommended to use
this for security (do in backend instead) or for front-end validation of user
inputs (use pattern=
instead).
Given an "allowed list" of comma separated strings, only permit values that
exist exactly the "allowed list". If the value does not exist within the
allowed list, it will produce an empty string (""
).
The fact that empty strings are "falsy" means you can chain together this
filter with the default filter in order to provide a fallback as well, which is
demonstrated in the third example. Examples:
Valid: apple
Invalid:
Invalid + default: Oops!
capfirst
Output given string with the first letter capitalized.
The Modulo.js framework is my favorite
default
If the given value is Falsy, use
the given default. Otherwise, use the value.
Fave snack: icecream
Snack count: none
Fave soda: guarana
divisibleby
Returns true
if the value is evenly divisible by the given argument.
Can 4 divide by 3?
false
4 is even
escapejs
Escape special characters in a given string using JavaScript (specifically,
JSON) escaping rules. If you have a string like "Hello\nWorld!"
(where \n
is a single new-line character), it will output
Hello\nWorld!
(that is, as "backslash-n" or two characters). It
will also "double-up" any backslashes it encounters.
Note that this is unrelated to the HTML autoescaping, meaning you may
need to also mark as |safe
if do not want it escaping for HTML
rules as well (e.g. changing <
, which is invalid for HTML text
but valid for JavaScript, into <
). Similarly, there is no
need to use this for escaping for HTML attribute values (e.g.
<input placeholder="{{ state.msg }}">
), as
the HTML escaping is sufficient. The only use is embedded JS strings.
Result: Just\ntrying\n\\stuff\\\nout
first
Retrieve the first item in an Array:
join
Formats an array to be comma-separated. Optionally, a different separator
can be specified as an argument.
Neymar, Maradona
Neymar + Maradona
json
Formats given data as a JSON string. It takes one optional argument, that if
specified, will cause indentation by the given amount.
[{"name":"Devante Adams"},{"name":"Steph Curry"},{"name":"Megan Rapinoe"}]
[
{
"name": "Devante Adams"
},
{
"name": "Steph Curry"
},
{
"name": "Megan Rapinoe"
}
]
last
Retrieve the last item in an Array:
length
Determine the length of the given value. This supports Strings, Arrays, and
Objects. For Objects, it will return the number of properties on the object.
Sentence length: 55
Flowers length: 2
Flights length: 3
lower
Display the given string in all lowercase letters.
Without: rAndOm cAPitalS
Lower: random capitals
pluralize
Allows for convenient pluralization in many human languages. This is for
words in languages where a plural form is used when there is "0" or "2+" items,
and a singular form for exactly 1 item. This is useful for the majority of noun
and verb conjugations in English, along with many other Indo-European
languages.
To use, give it a number (typically, the length of a list), and a comma
separated version of two forms of a word. If that number is exactly "1" it will
output the second form (or nothing at all, if the second form is not
specified), otherwise it will output the first form.
Below are some examples. Note that in the second example, it does not
specify a singular form, but instead only specifies an "s" to append to the
word "flower" to make it's plural "flowers".
We visited 4
cities
and picked 2
flowers
subtract
Subtracts the argument from the value. Example:
1337 hacks
42 is the answer
truncate
Cut off the given string after a number of characters specified by the
argument. If it has to cut off the string, it will append an "ellipsis"
character.
Long sentence: The sweat wis lashi…
Short word: Bird.
renderas
A highly useful filter that allows use of re-usable template snippets.
Typically in the format of
{{ state.data|renderas:trow }}
,
where state.data
is some Object, and there exists a template like
with the -name
"trow" that renders that object. The typical usage
of this is refactoring templates to conveniently render complex data. This
allows you to take complicated bits of template code and refactor it into
another smaller, helper, "child" template that is then used within the main
template.
The "input" to renderas should always be an Object. The keys and values of
that object will populate the "template variables" for that template, meaning
you can access them directly as template variables within the child template.
As an example, if we renderas
with an Object like
{altTitle: "Newest entry"}
, then within the child template
the "altTitle" becomes a "top-level" variable, and we can use syntax like
{{ altTitle }}
(no need for "." syntax).
Note that the child template will be isolated or "sandboxed" to
only have access to properties of the specified object. State, props,
script, etc will not be accessible as template variables. This sandboxing is
usually good and helps reduce bugs and makes child templates much easier to
read. However, sometimes you just want to quickly spit up and refactor a
template, meaning you want the child template to behave just like the parent,
and you want global variables available, just as they were available to the
parent. To include another template without any sandboxing, consider using the
include
template-tag.
Name
Type
Level
Jigglypuff
Normal
13
Pikachu
Electric
10
Ghastly
Ghost
8
reversed
Reverses the given input. Typically, this is used to reverse Arrays, such as
when you want to iterate over an Array in the opposite direction using a
{% for %}
template-tag. If the input is a String or another data
type, it will attempt to first convert to an Array.
Sunflowers, Marigolds
Concord
Richmond
Berkeley
Oakland
upper
Display the given string in all uppercase letters.
Without: rAndOm cAPitalS
Upper: RANDOM CAPITALS