Modulo [%] modulo.js
  • About
  • Start
  • Docs
v: 0.0.31
github | npm mdu.js

%

Documentation

  • Table of Contents
  • Tutorial
  • Templating
  • Template Reference
    • Built-in Template Tags
    • Built-in Filters
  • CParts
  • Lifecycle
  • Directives
  • Examples

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.

Note: This is meant for exploring features. Your work will not be saved.

 
7
 
1
<Template>
2
    {% comment "Optional note" %}
3
        <p>Commented out text that will be ignored
4
          {{ nonExistingVar|brokenFilter:"abc" }}</p>
5
    {% endcomment %}
6
    <p>After comment</p>
7
</Template>

After comment

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.

Note: This is meant for exploring features. Your work will not be saved.

 
15
 
1
<Template>
2
    {% for user in state.users %}
3
        <!-- (Hint: try removing the "comment" tags to enable the debugger -->
4
        {% comment %} {% debugger %} {% endcomment %}
5
        <p>{{ user.username }}</p>
6
    {% endfor %}
7
</Template>
8
​
9
<State
10
  users:='[
11
    {"username": "zer0c00l"},
12
    {"username": "ac1dburn", "id": 1337},
13
    {"username": "the_phantom_phr34k", "name": "Ramon"}
14
  ]'
15
></State>

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.

Note: This is meant for exploring features. Your work will not be saved.

 
12
 
1
<Template -name="other_template">
2
    <p>{{ state.testing }}</p>
3
</Template>
4
​
5
<Template>
6
    <h1>hello</h1>
7
    {% include other_template %}
8
</Template>
9
​
10
<State
11
    testing="hello world!"
12
></State>

hello

hello world!

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:

Note: This is meant for exploring features. Your work will not be saved.

 
15
 
1
<Template>
2
    <ul>
3
        {% for athlete in state.athletes %}
4
            <li>{{ athlete.name }}</li>
5
        {% endfor %}
6
    </ul>
7
</Template>
8
​
9
<State
10
  athletes:='[
11
    {"name": "Devante Adams"},
12
    {"name": "Steph Curry"},
13
    {"name": "Megan Rapinoe"}
14
  ]'
15
></State>
  • 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:

Note: This is meant for exploring features. Your work will not be saved.

 
15
 
1
<Template>
2
    <ul>
3
        {% for name, color in state.fave_colors %}
4
            <li><strong>{{ name }}</strong>: {{ color }}</li>
5
        {% endfor %}
6
    </ul>
7
</Template>
8
​
9
<State
10
    fave_colors:='{
11
      "Devante Adams": "Green",
12
      "Steph Curry": "Golden",
13
      "Megan Rapinoe": "Blue"
14
    }'
15
></State>
  • 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:

Note: This is meant for exploring features. Your work will not be saved.

 
13
 
1
<Template>
2
    <ul>
3
        {% for name, color in state.fave_colors %}
4
            <li><strong>{{ name }}</strong>: {{ color }}</li>
5
        {% empty %}
6
            No colors were found.
7
        {% endfor %}
8
    </ul>
9
</Template>
10
​
11
<State
12
    fave_colors:='{}'
13
></State>
    No colors were found.

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:

  • SimpleIf
  • IfElse

Note: This is meant for exploring features. Your work will not be saved.

 
9
 
1
<Template>
2
    {% if state.show %}
3
        Hello testing template world!
4
    {% endif %}
5
</Template>
6
​
7
<State
8
  show:=true
9
></State>
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:

Note: This is meant for exploring features. Your work will not be saved.

 
15
 
1
<Template>
2
    {% if state.athletes %}
3
        Athletes exists. Total athletes: {{ state.athletes|length }}
4
    {% elif state.benched %}
5
        Benched exists. Total benched: {{ state.benched|length }}
6
    {% else %}
7
        No athletes.
8
    {% endif %}
9
</Template>
10
​
11
<State
12
  benched:='[
13
    {"name": "Steph Curry"}
14
  ]'
15
></State>
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.

Note: This is meant for exploring features. Your work will not be saved.

 
9
 
1
<Template>
2
    {% if state.somevar == "x" %}
3
        This appears if variable somevar equals the string "x"
4
    {% endif %}
5
</Template>
6
​
7
<State
8
  somevar="x"
9
></State>
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.

Note: This is meant for exploring features. Your work will not be saved.

 
9
 
1
<Template>
2
    {% if state.somevar != "x" %}
3
        This appears if variable state.somevar does not equal the string "x".
4
    {% endif %}
5
</Template>
6
​
7
<State
8
  somevar="y"
9
></State>
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:

Note: This is meant for exploring features. Your work will not be saved.

 
11
 
1
<Template>
2
    {% if not state.show %}
3
        Do not show it!
4
    {% else %}
5
        Show it!
6
    {% endif %}
7
</Template>
8
​
9
<State
10
  show:=false
11
></State>
Do not show it!

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:

Note: This is meant for exploring features. Your work will not be saved.

 
9
 
1
<Template>
2
    {% if state.somevar lt 100 %}
3
        This appears if variable somevar is less than 100.
4
    {% endif %}
5
</Template>
6
​
7
<State
8
  somevar:=50
9
></State>
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:

Note: This is meant for exploring features. Your work will not be saved.

 
9
 
1
<Template>
2
    {% if state.somevar gt 100 %}
3
        This appears if variable somevar is greater than 100.
4
    {% endif %}
5
</Template>
6
​
7
<State
8
  somevar:=2600
9
></State>
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:

  • String
  • Array
  • Object

Note: This is meant for exploring features. Your work will not be saved.

 
9
 
1
<Template>
2
    {% if "B.C.E." in state.era %}
3
        This appears since "B.C.E." is a substring of "{{ state.era }}"
4
    {% endif %}
5
</Template>
6
​
7
<State
8
  era="3rd Century B.C.E. (Macedonia)"
9
></State>
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:

Note: This is meant for exploring features. Your work will not be saved.

 
13
 
1
<Template>
2
    {% if state.athletes|length gt 2 %}
3
        <p>There are more than 2 athletes!</p>
4
    {% endif %}
5
</Template>
6
​
7
<State
8
    athletes:='[
9
        {"name": "Devante Adams"},
10
        {"name": "Steph Curry"},
11
        {"name": "Megan Rapinoe"}
12
    ]'
13
></State>

There are more than 2 athletes!

#

Built-in Template Filters

add

Adds the argument to the value. Example:

Note: This is meant for exploring features. Your work will not be saved.

 
9
 
1
<Template>
2
    {{ state.value|add:7 }} hacks <br />
3
    {{ state.value|add:state.another }} hz
4
</Template>
5
​
6
<State
7
    value:=1330
8
    another:=1270
9
></State>
1337 hacks
2600 hz

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:


Note: This is meant for exploring features. Your work will not be saved.

 
9
 
1
<Template>
2
    Valid: {{ state.value|allow:"orange,apple,pear" }} <br />
3
    Invalid: {{ state.value|allow:"a,b,c" }} <br />
4
    Invalid + default: {{ state.value|allow:"a,b,c"|default:"Oops!" }}
5
</Template>
6
​
7
<State
8
    value="apple"
9
></State>
Valid: apple
Invalid:
Invalid + default: Oops!

capfirst

Output given string with the first letter capitalized.

Note: This is meant for exploring features. Your work will not be saved.

 
7
 
1
<Template>
2
    The {{ state.value|capfirst }} framework is my favorite
3
</Template>
4
​
5
<State
6
    value="modulo.js"
7
></State>
The Modulo.js framework is my favorite

default

If the given value is Falsy, use the given default. Otherwise, use the value.

Note: This is meant for exploring features. Your work will not be saved.

 
10
 
1
<Template>
2
    Fave snack: {{ state.snack|default:"icecream" }} <br />
3
    Snack count: {{ state.count|default:"none" }} <br />
4
    Fave soda: {{ state.soda|default:"Cola" }}
5
</Template>
6
<State
7
    snack=""
8
    count:=0
9
    soda="guarana"
10
></State>
Fave snack: icecream
Snack count: none
Fave soda: guarana

divisibleby

Returns true if the value is evenly divisible by the given argument.

Note: This is meant for exploring features. Your work will not be saved.

 
10
 
1
<Template>
2
    Can {{ state.value }} divide by 3? <br />
3
    {{ state.value|divisibleby:3 }} <br />
4
    {% if state.value|divisibleby:2 %}
5
        {{ state.value }} is even
6
    {% endif %}
7
</Template>
8
<State
9
    value:=4
10
></State>
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 &lt;). 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.

Note: This is meant for exploring features. Your work will not be saved.

 
10
 
1
<Template>
2
    Result: {{ state.value|escapejs }} <br />
3
</Template>
4
​
5
<State
6
    value="Just
7
trying
8
\stuff\
9
out"
10
></State>
Result: Just\ntrying\n\\stuff\\\nout

first

Retrieve the first item in an Array:

Note: This is meant for exploring features. Your work will not be saved.

 
7
 
1
<Template>
2
    <p>{{ state.athletes|first }}</p>
3
</Template>
4
​
5
<State
6
  athletes:='["Neymar", "Maradona"]'
7
></State>

Neymar

join

Formats an array to be comma-separated. Optionally, a different separator can be specified as an argument.

Note: This is meant for exploring features. Your work will not be saved.

 
8
 
1
<Template>
2
    <p>{{ state.athletes|join }}</p>
3
    <p>{{ state.athletes|join:" + " }}</p>
4
</Template>
5
​
6
<State
7
  athletes:='["Neymar", "Maradona"]'
8
></State>

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.

Note: This is meant for exploring features. Your work will not be saved.

 
12
 
1
<Template>
2
    <pre>{{ state.athletes|json }}</pre>
3
    <pre>{{ state.athletes|json:2 }}</pre>
4
</Template>
5
​
6
<State
7
    athletes:='[
8
        {"name": "Devante Adams"},
9
        {"name": "Steph Curry"},
10
        {"name": "Megan Rapinoe"}
11
    ]'
12
></State>
[{"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:

Note: This is meant for exploring features. Your work will not be saved.

 
7
 
1
<Template>
2
    <p>{{ state.athletes|last }}</p>
3
</Template>
4
​
5
<State
6
  athletes:='["Neymar", "Maradona"]'
7
></State>

Maradona

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.

Note: This is meant for exploring features. Your work will not be saved.

 
11
 
1
<Template>
2
    <p>Sentence length: {{ state.sentence|length }}</p>
3
    <p>Flowers length: {{ state.flowers|length }}</p>
4
    <p>Flights length: {{ state.flights|length }}</p>
5
</Template>
6
​
7
<State
8
    sentence="The sweat wis lashing oafay Sick Boy; he wis trembling."
9
    flowers:='["Marigolds", "Sunflowers"]'
10
    flights:='{"SFO": "LAX", "BAI": "MEX", "MEX": "MVD"}'
11
></State>

Sentence length: 55

Flowers length: 2

Flights length: 3

lower

Display the given string in all lowercase letters.

Note: This is meant for exploring features. Your work will not be saved.

 
8
 
1
<Template>
2
    <p>Without: {{ state.word }}</p>
3
    <p>Lower: {{ state.word|lower }}</p>
4
</Template>
5
​
6
<State
7
    word="rAndOm cAPitalS"
8
></State>

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".

Note: This is meant for exploring features. Your work will not be saved.

 
12
 
1
<Template>
2
    We visited {{ state.citynames|length}} 
3
    {{ state.citynames|length|pluralize:"cities,city" }}
4
​
5
    and picked {{ state.flowers|length}} 
6
    flower{{ state.flowers|length|pluralize:"s" }}
7
</Template>
8
​
9
<State
10
    flowers:='["Marigolds", "Sunflowers"]'
11
    citynames:='["Oakland", "Berkeley", "Richmond", "Concord"]'
12
></State>
We visited 4 cities and picked 2 flowers

subtract

Subtracts the argument from the value. Example:

Note: This is meant for exploring features. Your work will not be saved.

 
9
 
1
<Template>
2
    {{ state.value|subtract:3 }} hacks <br />
3
    {{ state.value|subtract:state.another }} is the answer
4
</Template>
5
​
6
<State
7
    value:=1340
8
    another:=1298
9
></State>
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.

Note: This is meant for exploring features. Your work will not be saved.

 
9
 
1
<Template>
2
    <p>Long sentence: {{ state.sentence|truncate:20 }}</p>
3
    <p>Short word: {{ state.word|truncate:20 }}</p>
4
</Template>
5
​
6
<State
7
    sentence="The sweat wis lashing oafay Sick Boy; he wis trembling."
8
    word="Bird."
9
></State>

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.

Note: This is meant for exploring features. Your work will not be saved.

 
24
 
1
<Template -name="trow">
2
    <div>{{ name }}</div> <div>{{ type }}</div> <div>{{ level }}</div>
3
</Template>
4
<Template>
5
    <details>
6
        <h3>Name</h3> <h3>Type</h3> <h3>Level</h3>
7
        {% for info in state.pokemon %}
8
            {{ info|renderas:trow }}
9
        {% endfor %}
10
    </details>
11
</Template>
12
<State
13
    pokemon:='[
14
        {"name": "Jigglypuff", "type": "Normal", "level": 13},
15
        {"name": "Pikachu", "type": "Electric", "level": 10},
16
        {"name": "Ghastly", "type": "Ghost", "level": 8}
17
    ]'
18
></State>
19
<Style>

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.

Note: This is meant for exploring features. Your work will not be saved.

 
11
 
1
<Template>
2
    <p>{{ state.flowers|reversed|join }}</p>
3
    {% for city in state.cities|reversed %}
4
        <p>{{ city }}</p>
5
    {% endfor %}
6
</Template>
7
​
8
<State
9
    flowers:='["Marigolds", "Sunflowers"]'
10
    cities:='["Oakland", "Berkeley", "Richmond", "Concord"]'
11
></State>

Sunflowers, Marigolds

Concord

Richmond

Berkeley

Oakland

upper

Display the given string in all uppercase letters.

Note: This is meant for exploring features. Your work will not be saved.

 
8
 
1
<Template>
2
    <p>Without: {{ state.word }}</p>
3
    <p>Upper: {{ state.word|upper }}</p>
4
</Template>
5
​
6
<State
7
    word="rAndOm cAPitalS"
8
></State>

Without: rAndOm cAPitalS

Upper: RANDOM CAPITALS

(C) 2023 - Michael Bethencourt - Documentation under LGPL 2.1