Spacebars templates
Spacebars is a handlebars-like templating language, built on the concept of rendering a reactively changing data context. Spacebars templates look like simple HTML with special “mustache” tags delimited by curly braces: {{ }}
.
As an example, consider the Todos_item
template from the Todos example app:
1 | <template name="Todos_item"> |
This template expects to be rendered with an object with key todo
as data context (we’ll see below how to enforce that). We access the properties of the todo
using the mustache tag, such as {{todo.text}}
. The default behavior is to render that property as a string; however for some attributes (such as checked={{todo.checked}}
) it can be resolved as a boolean value.
Note that simple string interpolations like this will always escape any HTML for you, so you don’t need to perform safety checks for XSS.
Additionally we can see an example of a template helper—{{checkedClass todo}}
calls out to a checkedClass
helper defined in a separate JavaScript file. The HTML template and JavaScript file together define the Todos_item
component:
1 | Template.Todos_item.helpers({ |
In the context of a Blaze helper, this
is scoped to the current data context at the point the helper was used. This can be hard to reason about, so it’s often a good idea to instead pass the required data into the helper as an argument (as we do here).
Apart from simple interpolation, mustache tags can be used for control flow in the template. For instance, in the Lists_show
template, we render a list of todos like this:
1 | {{#each todo in todos}} |
This snippet illustrates a few things:
- The
{{#each .. in}}
block helper which repeats a block of HTML for each element in an array or cursor, or renders the contents of the{{else}}
block if no items exist. - The template inclusion tag,
{{> Todos_item (todoArgs todo)}}
which renders theTodos_item
component with the data context returned from thetodosArg
helper.
You can read about the full syntax in the Spacebars. In this section we’ll attempt to cover some of the important details beyond just the syntax.
Data contexts and lookup
We’ve seen that {{todo.title}}
accesses the title
property of the todo
item on the current data context. Additionally, ..
accesses the parent data context (rarely a good idea), list.todos.[0]
accesses the first element of the todos
array on list
.
Note that Spacebars is very forgiving of null
values. It will not complain if you try to access a property on a null
value (for instance foo.bar
if foo
is not defined), but instead simply treats it also as null. However there are exceptions to this—trying to call a null
function, or doing the same within a helper will lead to exceptions.
Calling helpers with arguments
You can provide arguments to a helper like checkedClass
by simply placing the argument after the helper call, as in: {{checkedClass todo true 'checked'}}
. You can also provide a list of named keyword arguments to a helper with {{checkedClass todo noClass=true classname='checked'}}
. When you pass keyword arguments, you need to read them off of the hash
property of the final argument. Here’s how it would look for the example we just saw:
1 | Template.Todos_item.helpers({ |
Note that using keyword arguments to helpers is a little awkward, so in general it’s usually easier to avoid them. This feature was included for historical reasons to match the way keyword arguments work in Handlebars.
You can also pass the output of a helper to a template inclusion or other helper. To do so, use parentheses to show precedence:
1 | {{> Todos_item (todoArgs todo)}} |
Here the todo
is passed as argument to the todoArgs
helper, then the output is passed into the Todos_item
template.
Template inclusion
You “include” a sub-component with the {{> }}
syntax. By default, the sub-component will gain the data context of the caller, although it’s usually a good idea to be explicit. You can provide a single object which will become the entire data context (as we did with the object returned by the todoArgs
helper above), or provide a list of keyword arguments which will be put together into one object, like so:
1 | {{> subComponent arg1="value-of-arg1" arg2=helperThatReturnsValueOfArg2}} |
In this case, the subComponent
component can expect a data context of the form:
1 | { |
Attribute Helpers
We saw above that using a helper (or data context lookup) in the form checked={{todo.checked}}
will add the checked property to the HTML tag if todo.checked
evaluates to true. Also, you can directly include an object in the attribute list of an HTML element to set multiple attributes at once:
1 | <a {{attributes}}>My Link</a> |
1 | Template.foo.helpers({ |
Rendering raw HTML
Although by default a mustache tag will escape HTML tags to avoid XSS, you can render raw HTML with the triple-mustache: {{{ }}}
.
1 | {{{myHtml}}} |
1 | Template.foo.helpers({ |
You should be extremely careful about doing this, and always ensure you aren’t returning user-generated content (or escape it if you do!) from such a helper.
Block Helpers
A block helper, called with {{# }}
is a helper that takes (and may render) a block of HTML. For instance, we saw the {{#each .. in}}
helper above which repeats a given block of HTML once per item in a list. You can also use a template as a block helper, rendering its content via the Template.contentBlock
and Template.elseBlock
. For instance, you could create your own {{#if}}
helper with:
1 | <template name="myIf"> |
Built-in Block Helpers
There are a few built-in block helpers that are worth knowing about:
If / Unless
The {{#if}}
and {{#unless}}
helpers are fairly straightforward but invaluable for controlling the control flow of a template. Both operate by evaluating and checking their single argument for truthiness. In JS null
, undefined
, 0
, ''
, NaN
, and false
are considered “falsy”, and all other values are “truthy”.
1 | {{#if something}} |
1 | {{#if condition1}} |
Each-in
The {{#each .. in}}
helper is a convenient way to step over a list while retaining the outer data context.
1 | {{#each todo in todos}} |
Let
The {{#let}}
helper is useful to capture the output of a helper or document subproperty within a template. Think of it just like defining a variable using JavaScript let
.
1 | {{#let name=person.bio.firstName color=generateColor}} |
Note that name
and color
(and todo
above) are only added to scope in the template; they are not added to the data context. Specifically this means that inside helpers and event handlers, you cannot access them with this.name
or this.color
. If you need to access them inside a helper, you should pass them in as an argument (like we do with (todoArgs todo)
above).
Each and With
There are also two Spacebars built-in helpers, {{#each}}
, and {{#with}}
, which we do not recommend using (see prefer using each-in). These block helpers change the data context within a template, which can be difficult to reason about.
Like {{#each .. in}}
, {{#each}}
iterates over an array or cursor, changing the data context within its content block to be the item in the current iteration. {{#with}}
simply changes the data context inside itself to the provided object. In most cases it’s better to use {{#each .. in}}
and {{#let}}
instead, just like it’s better to declare a variable than use the JavaScript with
keyword.
Chaining of Block Helpers
You can chain block helpers:
1 | {{#input isRadio}} |
This is equivalent to:
1 | {{#input isRadio}} |
Strictness
Spacebars has a very strict HTML parser. For instance, you can’t self-close a div
(<div/>
) in Spacebars, and you need to close some tags that a browser might not require you to (such as a <p>
tag). Thankfully, the parser will warn you when it can’t understand your code with an exact line number for the error.
Escaping
To insert literal curly braces: {{ }}
and the like, add a pipe character, |
, to the opening braces:
1 | <!-- will render as <h1>All about {{</h1> --> |