Andrei Pall

Linux Software Engineering

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.