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:

  1. nothing,
  2. a value from the input source,
  3. the result of an expression, or
  4. 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:

  • < - &lt;
  • > - &gt;
  • & - &amp;
  • " - &quot;
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 contact not found error when rendering the template. To avoid the error condition, the template should be changed as follows (changes highlighted):

<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
  • $Root is 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).

Expressions