Understanding Spacebars

Spacebars is a Meteor template language inspired by Handlebars. It shares some of the spirit and syntax of Handlebars, but it has been tailored to produce reactive Meteor templates when compiled.
each
Iterates over an array or query cursor Example 1
An array of objects
Js
Template.list.helpers({
items: [
{ name: "foo", pet: "dog" },
{ name: "bar", pet: "cat" }
];
});
Template
<template name="list">
{{#each items}}
{{name}} - {{pet}}<br>
{{/each}}
</template>
Or each .. in
<template name="list">
{{#each item in items}}
{{item.name}} - {{item.pet}}<br>
{{/each}}
</template>
Result
foo - dog
bar - cat
Example 2
An array of strings
Js
Template.list.helpers({
items: [
"foo",
"bar"
]
});
Template
<template name="list">
{{#each items}}
{{this}}<br>
{{/each}}
</template>
Or each .. in
<template name="list">
{{#each item in items}}
{{item}}<br>
{{/each}}
</template>
Result
foo
bar
Example 3
A query cursor
Spacebars is smart enough to know how to iterate over a query cursor (and keep everything updated as things change).
Assuming that our Items collection currently has documents that have name and pet fields (just like example 1).
Js
Template.list.helpers({
items: function() {
return Items.find();
}
});
Template
<template name="list">
{{#each items}}
{{name}} - {{pet}}<br>
{{/each}}
</template>
Or each .. in
<template name="list">
{{#each item in items}}
{{item.name}} - {{item.pet}}<br>
{{/each}}
</template>
Result
foo - dog
bar - cat
with
Let’s highlight why we use this helper.
Js
Template.pet.helpers({
dog: {
name: 'Spot',
age: 3
}
});
Template Without with
<template name="pet">
name: {{dog.name}}<br>
age: {{dog.age}}
</template>
Template With with
<template name="pet">
{{#with dog}}
name: {{name}}
age: {{age}}
{{/with}}
</template>
with can also take an optional else statement which is used if dog is falsey
Js
Template.pet.helpers({
dog: false
});
Template
<template name="pet">
{{#with dog}}
name: {{name}}
age: {{age}}
{{else}}
There's no dog!
{{/with}}
</template>
While easy to overlook; as you can see above, with is very much like an if statement. If dog is falsey, the content contained within will not render.
if, else
Js
Template.pet.helpers({
dog: {
name: 'Spot',
age: 3,
sleeping: false
}
});
Template
<template name="pet">
{{#if dog.sleeping}}
{{dog.name}} is sleeping!
{{else}}
{{dog.name}} is awake!
{{/if}}
</template>
Result
Spot is awake!
unless
unless is the opposite of if
Js
Template.pet.helpers({
dog: {
name: 'Spot',
age: 3,
sleeping: false
}
});
Template
<template name="pet">
{{#unless dog.sleeping}}
{{dog.name}} is awake!
{{else}}
{{dog.name}} is sleeping!
{{/unless}}
</template>
Result
Spot is awake!
Template inclusion
Example 1
We can easily pull in another template to display. The most common usage is within an each block.
Js
Template.list.helpers({
items: [
{ name: 'car', colour: 'red' },
{ name: 'sword', colour: 'silver' },
{ name: 'beer', colour: 'amber' }
]
});
Templates
<template name="list">
{{#each items}}
{{> listItem}}
{{/each}}
</template>
<template name="listItem">
{{name}} - {{colour}}<br>
</template>
Result
car - red
sword - silver
beer - amber
Notice that our listItem template is automatically passed the current data context from our each block.
Example 2
Passing a data context
Js
Template.overview.helpers({
users: [
{ name: 'David' },
{ name: 'Shaune' }
]
});
Templates
<template name="overview">
{{> userList users}}
</template>
<template name="userList">
{{#each this}}
{{name}}<br>
{{/each}}
</template>
Result
David
Shaune
Example 3
Passing named arguments
Js
Template.overview.helpers({
users: [
{ name: 'David' },
{ name: 'Shaune' }
]
});
Template
<template name="overview">
{{> userList users=users title="Top users!"}}
</template>
<template name="userList">
<p>{{title}}</p>
{{#each users}}
{{name}}<br>
{{/each}}
</template>
Result
Top users!
David
Shaune
Helpers that take arguments
Js
Template.articles.helpers({
items: [
{
title: 'Good article',
status: 'important',
content: 'A really good article'
},
{
title: 'Normal article',
status: 'normal',
content: 'A good article'
}
]
});
Template.article.helpers({
statusIs: function(status) {
return this.status === status;
}
});
Template
<template name="articles">
{{#each items}}
{{> article}}
{{/each}}
</template>
<template name="article">
{{#if statusIs "important"}}
<strong>{{title}}</strong>
{{else}}
{{title}}
{{/if}}
<p>{{content}}</p>
</template>
Custom block helpers
Template
<template name="header">
<header>{{> Template.contentBlock}}</header>
</template>
<template name="examplePage">
{{#header}}Example Page{{/header}}
</template>
Template
Example with an else block
<template name="header">
<header>
{{#if ready}}
{{> Template.contentBlock}}
{{else}}
{{> Template.elseBlock}}
{{/if}}
</header>
</template>
<template name="examplePage">
{{#header ready=Template.subscriptionsReady}}
Example Page
{{else}}
Loading...
{{/header}}
</template>
Comments
Template
{{! Comments can be one line}}
{{!
Or they can be
multi-line
}}
{{!-- They can also be written like this --}}
Template.dynamic
Js
Template.example.helpers({
template: 'test'
});
Template
<template name="example">
{{> Template.dynamic template=template}}
</template>
<template name="test">
Hello World!
</template>
This operator allows you to render templates along with their data context dynamically.