Mustache Templates
The glue
between information and presentation is a template system. PlanetGIS has a modified version of
the Mustache
template system, so named because of the use of {{..}} tags which resemble a
sideways moustache. A description of the standard Mustache template system can be found here:
mustache.github.io/mustache.5.html
A Mustache template is an HTML document (though it could also be other types of documents) with embedded Mustache
{{..}} tags that are used to insert information into the document. The source of information is a JSON object,
which we describe in the next chapter.
Syntax
Similar to a Markup Language, Mustache works with tags
. In order to not interfere with HTML
tags, Mustache uses {{..}} instead of <..>. Data
tags are standalone, like an
<img> in HTML, but section
tags are pairs of an opening tag and a closing tag with the same
solidus (forward slash) / placement as in HTML: {{/..}}
All tags except regular data tags start with a control character
immediately after the
{{, which determines the tag's function. The control character is then followed by either a key
or an
expression
, which determines its value
. Spaces are allowed after the control character, if only for the purpose
of better readability.
Closing tags omit the control character, but must have identical keys/expressions after the / to match them with their opening tags.
The content
of a section tag refers to all the (HTML) text and nested Mustache tags between the opening and
closing tags. Tags (including their content) are always replaced when the template is rendered
into the final document.
Tags will be replaced by one of four things:
- nothing,
- a value from the input source,
- the result of an expression, or
- a repetition of the tag's content, each repetition using
context
from the next item in a data array.
Planet's implementation of Mustache is case-sensitive: keys must match members in data objects exactly (case-sensitively) and closing tags must match opening tags similarly.
Data tags are replaced by their value, but section tags conditionally (and perhaps repeatedly) output their content depending on their value.
Control characters
- # (hash) — regular section
- ? (question mark) — conditional section
- ^ (caret) — inverted section
- / (solidus) — end of section
- & (ampersand) — HTML-encoded data; alternative syntax:
{{{..}}} - % (percent) — URL-encoded data
- ! (exclamation mark) — comment
Data tags
Data tags are standalone (no opening and closing pair) and contain either keys or expressions that will determine their values. A key must match the name of a member in the input data (JSON) object. If the data tag contains an expression, it will be replaced by the result of the expression (which could be nothing).
An example template (excluding HTML tags for clarity):
{{name}}
{{company}}
{{email}}
Input data object:
{
"name": "John Smith",
"company": "ACME Corp",
"email": "john@acme.com"
}
Final output:
John Smith ACME Corp john@acme.com
HTML encoding
Since the primary function of the Mustache template system is to be used in HTML documents, a regular data tag is replaced by a value that will not interfere with HTML tags. The following characters will be substituted with HTML character references:
- < -
< - > -
> - & -
& - " -
"
URL encoding
If you are using Mustache to substitute data inside a link (the value of the href=".." attribute of an
<a> tag), the substituted value must be URL encoded
instead. Use the % control character
to change the tag's function to a URL tag: {{%..}}
HTML tags in your source data
If you have HTML tags in your source data (the way this documentation is constructed), you'll want Mustache to pass through
the value unaltered. This means you must ensure the source data contains valid HTML and your HTML tags are correctly closed,
or the substitution might break your entire document following the tag. There are two ways to make a Mustache tag unescaped
:
- Use the & control character:
{{&..}} - Use three { and }'s around the tag:
{{{..}}}
Mustache comments
You can place comments in a Mustache tag that will not be included in the final document. (HTML comments will still be in
the final document, though not visible to the reader). This is useful to leave notes to yourself or
another person working with the template. A Mustache comment has a ! control character, for example:
{{! This won't be in the source of the final document }}
You can temporarily hide a Mustache data tag by changing its control character to !, for example in cases
where the corresponding member is not available in the input object. To hide a section, you would need to remove the closing
}} and ensure there are no nested Mustache tags, which will cause the comment to end earlier than the end tag of
the section.
Sections
Sections have an opening tag that must be paired with a closing tag. The text between the opening and closing tag is the
section's content
and can be HTML (or something else) as well as further nested Mustache tags. The closing tag must have
the solidus (forward slash) right after the {{, followed by a key or expression that must be an exact match of
the opening tag in order to pair them.
Section tags render
their contents zero or more times into the final output, depending on the source data identified
by the section tag's key or expression.
Regular sections
A regular section tag has the # control character, e.g. {{#..}} and is an instruction to include
the tag's content once for each item in its value. Unlike traditional Mustache, if the tag contains a key, it must be found
in the source data object (and match case-sensitively). To get the traditional behaviour, see Conditional sections
below.
If the value is empty
, nothing (including the tags) will appear in the final result. A value is empty
(also called falsey
) if it is an empty object, list or string, the number 0, a false or a null. Otherwise, the tag's
content is output to the document once for each item in the value, if the value is a list, or once if the value is not a list.
Inverted sections
To obtain the opposite of a regular section, similar to an else in a programming language, you can use an
inverted section
. An inverted section has a ^ control character and is an instruction to output the tag's
content if its value is empty
or not found.
Here is an example of a regular section and an inverted section:
Template:
{{company}}
{{#contact}}
The contact person's name is {{name}}, who can be reached at {{email}}
{{/contact}}
{{^contact}}
There is no contact person listed for {{company}}
{{/contact}}
Data object, scenario 1:
{
"company": "ACME Corp",
"contact":
{
"name": "John Smith",
"email": "john@acme.com"
}
}
Output, scenario 1:
ACME Corp The contact person's name is John Smith, who can be reached at john@acme.com
Data object, scenario 2:
{
"company": "ACME Corp",
"contact": null
}
Output, scenario 2:
ACME Corp There is no contact person listed for ACME Corp
This example assumed a plain text template to be displayed in Notepad. An HTML template will look like this:
<h5>{{company}}</h5>
{{#contact}}
<p>The contact person's name is <b>{{name}}</b>, who can be reached at <i>{{email}}</i></p>
{{/contact}}
{{^contact}}
<p>There is no contact person listed for {{company}}</p>
{{/contact}}
HTML output with data object of scenario 1:
ACME Corp
The contact person's name is John Smith, who can be reached at john@acme.com
Conditional sections
A conditional section
has a ? control character and functions like a query to see if a key exists in the
input object. If a key is not found, the section is removed from the final output. If the key is found but its value is
falsey
(null, for example), the section is also removed like with a regular section.
Conditional sections do not switch context to inner objects, so you still need a regular section inside the conditional section in order to refer to the members without using extra qualifiers.
In the above example, a third scenario for the data object is the omission of the contact member:
{
"company": "ACME Corp",
}
This will cause a member
error when rendering the template. To avoid the error condition, the
template should be changed as follows (changes highlighted):contact
not found
<h5>{{company}}</h5>
{{?contact}}
<p>The contact person's name is <b>{{contact.name}}</b>, who can be reached at <i>{{contact.email}}</i></p>
{{/contact}}
{{^contact}}
<p>There is no contact person listed for {{company}}</p>
{{/contact}}
Because a conditional section does not do a context switch
to the contact object, member names in the data
tags are further qualified, e.g. contact.name The alternative would be to nest a regular section,
for example:
<h5>{{company}}</h5>
{{?contact}}
{{#contact}}
<p>The contact person's name is <b>{{name}}</b>, who can be reached at <i>{{email}}</i></p>
{{/contact}}
{{/contact}}
{{^contact}}
<p>There is no contact person listed for {{company}}</p>
{{/contact}}
Differences to standard Mustache
Missing members result in an error
Standard Mustache treats missing members as falsey
and ignores them (replaces them with nothing).
In PlanetGIS, source objects are usually generated
by SQL statements, which makes the generated JSON fairly consistent. However, if the
SQL statements are changed, result columns may disappear, and tags referring to them will not be known about. Therefore, Planet
generates an error message when this happens so that either the SQL or the template can be corrected. The only case where
members may disappear from a data object without it being an issue of tags not matching object members, is when the SQL was
used to create an object (as opposed to an array) and returned no rows, in which case all members disappear.
Conditional sections
To avoid the above type of error, use a conditional section with a key corresponding to a member that won't be null,
for example, a member that originates from a primary key in the database. (There is also a method to check for empty objects with an expression,
as we will see below.) Planet's conditional sections act more like traditional Mustache sections (allowing for missing members
to be treated as falsey
, and quietly ignored), but Planet's conditional sections do not do context switching, so you
may still need to use a nested regular section.
Inverted sections do not do a context switch either (there is nothing to switch to since if it referred to an object, that would have been empty to trigger the inverted section), so inverted sections are the opposite of conditional sections, not regular sections.
Other differences
- URL tags, described above.
- Qualified member names, described below.
- JSON expressions, described below.
Keys
A key
is another, more specific, term for the name of a member in a JSON object. In a Mustache tag, the key is
the name inside the tag, after the control character, unless it is a comment or expression. In this section, we will use
key
and member name
interchangeably.
Scope
Scope
is a programming language concept that refers to the accessibility of data members by a using their name.
Qualifying
the name of a data member is to be more specific as to where to look for it. Mustache, like JavaScript,
Pascal, C and SQL, uses a . between names to be more specific.
Context switching
The example above showed a context switch
. The template starts with a {{company}} tag, for which
it needs a company member in the root of the data object. The following {{#contact}} section tag
corresponds to the contact member, also in the root of the data object. If the value of the contact member was a simple
string or number, the section would just be conditional, but because the value is an object, the template engine makes a
context switch to that object. The tags nested in this section will now find the members of the contact object.
If the value of contact was an array of objects, the section would be repeated for each object, making a context switch to each.
In a Mustache template, the current context
is the specific object inside a larger data object that can be accessed
by only using the names of that object's members (without qualifying them). A regular section switches the context, meaning
new members (of the object referred to in the section tag's key) comes into scope while members previously in scope
get a lower priority when searching for them by name.
Outer scope
Since Mustache searches upwards
, or rather outwards
if a key is not found, that means outer
(container)
objects are always in scope
, and their members may therefore be accessed by name. For example, the tag {{company}}
can be used inside the {{#contact}} section to insert the company name.
A problem arises when outer objects have the same keys as inner objects. The only way to access those members is to be more specific, i.e. by qualifying them.
Qualified keys
Inner qualification
In the second-last example a context switch was avoided by qualifying each key (contact.name and
contact.email). That was inner
qualification.
Self qualification
To be very specific that you are referring to a key in the current context, you can precede a key with a .,
but the key also has to be quoted, which we discuss in the next section. Assuming a context switch has occurred, as in
the last example, the following will ensure that a tag references only a key in the contact object:
{{$.name}}. The advantage to this is that an error condition won't be masked by the existence of the same key
in an outer object (quite likely with a key named name
).
Outer qualification
To refer to a key in an object that contains the current context, and specifically only one level up
(or out
)
the key can be prefixed by two .s: {{$..name}}. Each additional . specifies another level outward.
Full qualification
A fully qualified
key contains the complete path to it, from the root of the object. The root object, or
outermost
object, can be specified with $Root and therefore the fully qualified name of the
root object's company member is $Root.company and the deeper name member $Root.contact.name
Quoting keys
Partly because of their potential use in expressions (see next section), there needs to be restrictions on the characters that can be used in a key, as well as a method to quote keys that contain restricted characters. Keys do not need to be quoted if they start with a $, _ or a letter and may contain digits from the second character onwards. All characters past Unicode code point 127 count as letters. Keys containing spaces, special characters or start with a digit must be quoted.
Dollar quoting
Keys may be quoted with SQL-style " after a $. For example, $"company name" tells the
Mustache expression parser that the value is not a string, but a key that must be found in a JSON object that might look like
this: { "company name": "ACME Corp." }. Like with SQL, a " inside a name must be doubled.
Like the double-quotes, the dollar character is not part of the actual key, however, if the $ is followed by
a letter or _ it is treated as part of the key. PlanetGIS uses $keys
for JSON object and array members
created by SQL, so $keys are common.
Special key names
$Rootis a special key name that refers to the whole (outer-most) JSON object.$.refers to the entire object that is currently selected (self).$..refers to the entire object that contains the currently selected object (parent).$...additional dots for levels up to the root (ancestor).