Helpers

  • page
can.stache.Helpers  

Helpers

Helpers are functions that can be registered and called from within templates. While stache is intended to be logic-less, helpers enable the execution of logic from within a stache template.

Stache includes a number of built-in helpers, but custom helpers can be registered as well. The majority of these built-in helpers have [can.stache.Basics basic tag] equivalents.

Built-in Helpers

The {{#if key}} helper is used for if statements. The if helper is equivalent to using a {{#key}} section. If they key passed to the helper is truthy, the section will be rendered.

Template: 
    {{#if friends}}
        I have friends!
    {{/if}}

Data: 
    {
        friends: true
    }

Result:
    I have friends!

When using the {{#if key}} helper, or any other helper for that matter, the special {{else}} helper becomes available.{{else}}` is equivalent to an [can.stache.helpers.inverse {{^key}}] inverse section (rendering falsey data), except that it only uses a single tag and exists inside a helper section.

Template: 
    <ul>
        {{#if friends}}
            </li>{{name}}</li>
        {{else}}
            <li>No friends.</li>
        {{/if}}
    </ul>

Data: 
    {
        friends: false
    }

Result:
    <ul>
        <li>No friends.</li>
    </ul>

The {{#unless key}} helper is equivalent to using a [can.stache.helpers.inverse {{^key}}] inverse section. If they key passed to the helper is falsey, the section will be rendered.

Template: 
    {{#unless friends}}
        You don't have any friends!
    {{/unless}}

Data: 
    {
        friends: []
    }

Result:
    You don't have any friends!

The {{#each key}} helper is equivalent to using a {{#key}} section for iterating an array. In this case, the entire array will be rendered using the inner text item by item.

Template: 
    <ul>
        {{#each friends}}
            <li>{{name}}</li>
        {{/each}}
    </ul>

Data: 
    { 
        friends: [ 
            { name: "Austin" }, 
            { name: "Justin" } 
        ] 
    }

Result:
    <ul>
        <li>Austin</li>
        <li>Justin</li>
    </ul>

The {{#with key}} helper is equivalent to using a {{#key}} section for regular objects. The helper will change the current context so that all tags inside will look for keys on the local context first.

Template: 
    <h1>Hi {{name}}</h1>
    {{#with friend}}
        <p>You have a new friend: {{name}}</p>
    {{/with}}

Data: 
    {
        name: "Andy",
        friend: { name: "Justin" } 
    }

Result:
    <h1>Hi Austin</h1>
    <p>You have a new friend: Justin</p>

When using the [can.mustache.helpers.is {{#is key1 key2}}] helper you can simply compare key1 and key2. If the result of comparsion is truthy, the section will be rendered.

Template: 
    <ul>
    {{#is name 'Alex'}}
        </li>Your name is {{name}}</li>
    {{else}}
        <li>Your name is not Alex!</li>
    {{/is}}
    </ul>

Data: 
    {
        name: 'John'
    }

Result:
    <ul>
        <li>Your name is not Alex!</li>
    </ul>

The {{data key}} helper is another special helper for data associations that will save the current context on the active DOM element with can.data.

Template:
    <ul>
        <li id="personli" {{data 'person'}}>{{name}}</li>
    </ul>

Data:
    {
        name: 'Austin'
    }

The data can be retrieved later with:
    var nameObject = can.data(can.$('#personli'), 'person'); 

Registering Helpers

You can register your own global helper with the registerHelper method, or a local helper (just accessible by the template being rendered) by passing in an object containing helper functions to can.view.

Localization is a good example of a custom helper you might implement in your application. The below example takes a given key and returns the localized value using jQuery Globalize.

can.stache.registerHelper('l10n', function(str, options){
    return Globalize != undefined 
        ? Globalize.localize(str) 
        : str;
});

Or another way to do this:

can.view("//path/to/template.stache", data, {
    l10n: function(str, options){
        return Globalize != undefined 
            ? Globalize.localize(str) 
            : str;
    }
})

In the template, invoke the helper by calling the helper name followed by any additional arguments.

Template:
    <span>{{l10n 'mystring'}}</span>

Result:
    <span>my string localized</span>

Helpers with can.Map attributes

If a can.Map attribute is passed as an argument to a helper, it is converted to a can.compute getter/setter function. This is to allow creating 2-way binding type functionality between a can.Map attribute and a form element. For example in your template:

<div>{{addPrefix name}}</div>

Your helper would look like:

var item = new can.Map({name: "Brian"}),
    frag = can.view("#template", item, {
      addPrefix: function(name){
        return "Mr." + name()
      }
    });

Note we're calling name() in order to read its contents.

Multiple Arguments

You can pass multiple arguments just by putting a space between that and the previous argument like so:

{{helper 'cat' 'hat'}}

can.stache.registerHelper('helper', function(arg1, arg2, options){
    // arg1 -> 'cat'
    // arg2 -> 'hat'
});

Evaluating Helpers

If you want to use a helper with a section, you need to call options.fn(context) in your return statement. This will return a string with the resulting evaluated section.

Similarly, you can call options.inverse(context) to evaluate the template between an {{else}} tag and the closing tag.

For example, when a route matches the string passed to our routing helper it will show/hide the text.

can.stache.registerHelper('routing', function(str, options){
    if (can.route.attr('filter') === str)
        return options.fn(this);
    }
});

{{#routing 'advanced'}}
    You have applied the advanced filter.
{{/routing}}

Advanced Helpers

Helpers can be passed normal objects, native objects like numbers and strings, as well as a hash object. The hash object will be an object literal containing all ending arguments using the key=value syntax. The hash object will be provided to the helper as options.hash. Additionally, when using section with the helper, you can set a custom context by passing the object instead of this.

can.stache.registerHelper('exercise', function(group, action, 
                                        num, options){
    if (group && group.length > 0 && action && num > 0) {
        return options.fn({
            group: group,
            action: action,
            where: options.hash.where,
            when: options.hash.when,
            num: num
        });
    }
    else {
        return options.inverse(this);
    }
});

{{#exercise pets 'walked' 3 where='around the block' when=time}}
    Along with the {{#group}}{{.}}, {{/group}}
    we {{action}} {{where}} {{num}} times {{when}}.
{{else}}
    We were lazy today.
{{/exercise}}

{
    pets: ['cat', 'dog', 'parrot'],
    time: 'this morning'
}

This would output:

Along with the cat, dog, parrot, we walked around the block 
3 times this morning.

Whereas an empty data object would output:

We were lazy today.