-
Notifications
You must be signed in to change notification settings - Fork 1
mustache
@page Mustache Mustache @parent Tutorial 5
@body Mustache provides logic-less templates that will live-bind to Observes. You can get Mustache functionality as a plugin for CanJS. Mustach and Handlebar templates are both compatible with CanJS's Mustache implementation, so you can reuse any existing templates you have.
Mustache is easy to get started with. Templates look like normal HTML, except they contain special tags for inserting data into the template, iterating through lists, and filtering blocks.
Here's an example of a template that might render a list of Todos:
@codestart <script type="text/mustache" id="todosList"> {{#todos}} <li>{{description}}</li> {{/todos}} </script> @codeend
And you can use can.view to render the template:
@codestart Todo.findAll({}, function(todos) { document.getElementById('list') .appendChild(can.view('todoList', {todos: todos})); }); @codeend
There are three kinds of magic tags used in Mustache:
-
{{ }}will HTML-escape the value enclosed inside the tags and write it to the template. -
{{{ }}}will write the value enclosed inside the tags and write it directly to the template without escaping it. -
{{! }}will write nothing to the template and is mostly used for comments.
Keys insert data into the template. They reference variables in the current context.
This template:
@codestart Name: {{name}} @codeend
given this data:
@codestart {name: 'Alice'} @codeend
will render thusly:
@codestart Name: Alice @codeend
Sections contain text blocks and are conditionally rendered based on the value enclosed in the opening tag. They also change the context inside them to the key referenced in the opening tag.
For the following examples, we will assume the template is being populated with this set of data:
@codestart { name: 'Alice Liddell', nickname: '', friends: ['Bob', 'Eve'], enemies: [] } @codeend
If the key is undefined, null, false, '', or [], it is considered
a falsey value and the section is not rendered at all. Neither of these sections
will render:
@codestart {{#enemies}} <li>{{.}}</li> {{/enemies}} @codeend
@codestart {{#nickname}}{{.}}{{/nickname}} @codeend
If the key is a non-empty array, the section will be rendered once for each element in the array. If it is truthy but not an array, the section is rendered once.
This template:
@codestart <h1>{{#name}}{{.}}{{/name}}</h1> <ul> {{#friends}} <li>{{.}}</li> {{/friends}} </ul> @codeend
will render like this:
@codestart <h1>Alice Liddell</h1> <ul> <li>Bob</li> <li>Eve</li> </ul> @codeend
You can also make inverted sections that render if the key referenced in the opening tag is falsey:
@codestart <ul> {{#friends}} <li>{{.}}</li> {{/friends}} {{^friends}} <li>You have no friends.</li> {{/friends}} </ul> @codeend
When Mustache is resolving an object in a section, it sets the current context
to the object value for which it is iterating. (If the key in the opening tag
of a section was not an array, it sets the context to the value of that key.)
You can reference the current context as ..
Internally, Mustache keeps a stack of contexts as the template enters nested sections and helpers. If a key is not found in the current context, Mustache will look for the the in each successive parent context until it resolves the key or runs out of parent contexts.
For example, with this data:
@codestart { brothers: [{name: 'Bob'}, {name: 'David'}], sisters: [{name: 'Alice'}, {name: 'Eve'}] } @codeend
and this template:
@codestart {{#brothers}} {{#sisters}} {{name}} {{/sisters}} {{/brothers}} @codeend
the rendered result will be:
@codestart Alice Eve Alice Eve @codeend
Since there is no sisters key in the context of the elements of the brothers
array, Mustache jumps up to the parent context and resolves sisters there.
Mustache lets you register functions to be called from inside the template called helpers. Since Mustache template are logic-less, all your view logic will be inside helpers.
To use a helper that is local to the template you're rendering, pass it as the
third argument to can.view in an object where the key is the name of the helper
and the value is the helper function:
@codestart var fragment = can.view('todosList', {todos: todos}, { uppercase: function(str) { return str.toUppercase(); } }); @codeend
This might be used in a template like this:
@codestart <script type="text/mustache" id="todosList"> {{#todos}} <li>{{uppercase description}}</li> {{/todos}} </script> @codeend
If a property of an observe is passed to a helper function, the helper will become a can.compute. As an example, if you had this template:
@codestart <script type="text/mustache" id="prefixedName"> <div>{{addMs lastName}}</div> </script> @codeend
And you ran this code:
@codestart var name = new can.Observe({firstName: 'Alice', lastName: 'Liddell'}); document.getElementById('name') .appendChild(can.view('prefixedName', name, { addMs: function(lastName) { return 'Ms. ' + lastName; } })); name.attr({firstName: 'Allison', lastName: 'Wonderland'}); @codeend
The contents of the <div> would be Ms. Wonderland.
You can register global helpers using can.Mustache.registerHelper:
@codestart can.Mustache.registerHelper('i10n', function(str, options) { return (Globalize != undefined ? Globalize.localize(str) : str); }); @codeend
You can use the data helper in Mustache to associate data to an element. This
helper will associate the current context (.) with a key you pass to the
helper.
For example, this template:
@codestart <script type="text/mustache" id="nameDiv"> <div id="person" {{data 'name'}}>{{firstName}} {{lastName}}</div> </script> @codeend
lets you do this in code:
@codestart document.body.appendChild(can.view('nameDiv', { firstName: 'Alice', lastName: 'Liddell' }));
var obj = can.data(document.getElementById('person'), 'name'); obj; // { firstName: 'Alice', lastName: 'Liddell'} @codeend
You can embed templates in other templates by using partials. Partials inherit
the context where they are called. They are evaluated at render time, so you
should be careful to avoid infinite loops. To include a partial, put its URL or
ID inside {{> }}.
With these templates: @codestart <script type="text/mustache" id="names"> <ul> {{#names}} {{>user}} {{/names}} </ul> </script> <script type="text/mustache" id="user"> <li>{{firstName}} {{lastName}}</li> </script> @codeend
the expanded template at render time would look similar to:
@codestart <ul> {{#names}} <li>{{firstName}} {{lastName}}</li> {{/names}} </ul> @codeend