DoneJS StealJS jQuery++ FuncUnit DocumentJS
6.0.1
5.33.2 4.3.0 3.14.1 2.3.35
  • About
  • Guides
  • API Docs
  • Community
  • Contributing
  • Bitovi
    • Bitovi.com
    • Blog
    • Design
    • Development
    • Training
    • Open Source
    • About
    • Contact Us
  • About
  • Guides
  • API Docs
    • Observables
      • can-bind
      • can-compute
      • can-debug
      • can-deep-observable
      • can-define
      • can-define/list/list
      • can-define/map/map
      • can-define-backup
      • can-define-stream
      • can-define-stream-kefir
      • can-event-queue
      • can-kefir
      • can-list
      • can-map
      • can-map-compat
      • can-map-define
      • can-observable-array
      • can-observable-object
      • can-observation
      • can-observation-recorder
      • can-observe
      • can-simple-map
      • can-simple-observable
      • can-stream
      • can-stream-kefir
      • can-value
    • Views
      • can-attribute-observable
      • can-component
      • can-observable-bindings
      • can-stache
        • Tags
          • {{expression}}
          • {{{expression}}}
          • {{#expression}}
          • {{/expression}}
          • {{else}}
          • {{<partialName}}
          • {{!expression}}
          • {{-expression-}}
        • Helpers
          • and
          • console
          • debugger
          • domData
          • eq
          • for(of)
          • portal
          • if
          • joinBase
          • let
          • not
          • or
          • switch
          • case
          • default
        • Expressions
          • Bracket Expression
          • Call Expression
          • Hash Expression
          • KeyLookup Expression
          • Literal Expression
        • Methods
          • addBindings
          • addConverter
          • addHelper
          • addLiveHelper
          • from
          • safeString
        • Key Operators
          • ~compute
          • ./current
          • ../parent
          • scope
          • scope/key
          • this
          • key
        • Pages
          • Expressions
          • Helpers
        • Types
          • getterSetter
          • helper
          • helperOptions
          • sectionRenderer
          • simpleHelper
          • view
        • Deprecated
          • Helper Expression
          • scope.vars
          • {{data name}}
          • {{#each(expression)}}
          • {{#is(expressions)}}
          • {{#unless(expression)}}
          • {{#with(expression)}}
          • registerConverter
          • registerHelper
          • registerPartial
          • Legacy Scope Behavior
          • {{^expression}}
          • {{>key}}
      • can-stache-bindings
      • can-stache-converters
      • can-stache-element
      • can-stache-route-helpers
      • can-view-autorender
      • can-view-callbacks
      • can-view-import
      • can-view-model
      • can-view-parser
      • can-view-scope
      • can-view-target
      • steal-stache
    • Data Modeling
      • can-connect
      • can-connect-feathers
      • can-connect-ndjson
      • can-connect-tag
      • can-define-realtime-rest-model
      • can-define-rest-model
      • can-fixture
      • can-fixture-socket
      • can-local-store
      • can-memory-store
      • can-ndjson-stream
      • can-query-logic
      • can-realtime-rest-model
      • can-rest-model
      • can-set-legacy
      • can-super-model
    • Routing
      • can-deparam
      • can-param
      • can-route
      • can-route-hash
      • can-route-mock
      • can-route-pushstate
    • JS Utilities
      • can-assign
      • can-define-lazy-value
      • can-diff
      • can-globals
      • can-join-uris
      • can-key
      • can-key-tree
      • can-make-map
      • can-parse-uri
      • can-queues
      • can-string
      • can-string-to-any
      • can-zone-storage
    • DOM Utilities
      • can-ajax
      • can-attribute-encoder
      • can-child-nodes
      • can-control
      • can-dom-data
      • can-dom-events
      • can-dom-mutate
      • can-event-dom-enter
      • can-event-dom-radiochange
      • can-fragment
    • Data Validation
      • can-define-validate-validatejs
      • can-type
      • can-validate
      • can-validate-interface
      • can-validate-legacy
      • can-validate-validatejs
    • Typed Data
      • can-cid
      • can-construct
      • can-construct-super
      • can-data-types
      • can-namespace
      • can-reflect
      • can-reflect-dependencies
      • can-reflect-promise
      • can-types
    • Polyfills
      • can-symbol
      • can-vdom
    • Core
    • Infrastructure
      • can-global
      • can-test-helpers
    • Ecosystem
    • Legacy
  • Community
  • Contributing
  • GitHub
  • Twitter
  • Chat
  • Forum
  • News
Bitovi

can-stache

  • npm package badge
  • Star
  • Edit on GitHub

Live binding templates.

stache([name,] template)

Processes the template string and returns a view function that can be used to create HTML elements with data.

import {stache} from "can";

// parses the template string and returns a view function:
const view = stache(`<h1>Hello {{this.subject}}</h1>`);

// Calling the view function returns HTML elements:
const documentFragment = view({subject: "World"});

// Adds those elements to the page
document.body.appendChild( documentFragment );

console.log(document.body.innerHTML) //-> "<h1>Hello World</h1>";

stache is most commonly used by can-stache-element to define a component's view:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
  static view = `
    <h1>Hello {{this.subject}}</h1>
  `;

  static props = {
    subject: {default: "World"}
  };
};
customElements.define("my-demo", MyDemo);
</script>

Use steal-stache to import template view functions with StealJS.

Use can-stache-loader to import template view functions with webpack.

Parameters

  1. name {String}:

    Provides an optional name for this type that will show up nicely in errors. Files imported with steal-stache will use their filename.

  2. template {String}:

    The text of a stache template.

Returns

{view(data, helpers)}:

A view function that returns a live document fragment that can be inserted in the page.

Purpose

Stache templates are used to:

  • Convert data into HTML.
  • Update the HTML when observable data changes.
  • Enable custom elements and event and data bindings.

Stache is designed to be:

  • Safe. It does not use eval in any form of its use.
  • Easy for beginners to understand - It looks a lot like JavaScript.
    {{# for( item of this.items ) }}
       <li>
         <span>{{ item.name }}</span>
         <label>{{ this.getLabelFor(item) }}</label>
       </li>
    {{/ }}
    
  • Limited - Complex logic should be done in the ViewModel where it is more easily tested. Stache only supports a subset of JavaScript expressions.
  • Powerful (where you want it) - Stache adds a few things JavaScript doesn't support but are very useful for views:
    • Stache tolerates undefined property values - The following will not error. Instead stache will simply warn:
      {{this.property.does.not.exist}}
      
    • Stache is able to read from promises and other observables directly:
      {{# if(promise.isPending) }} Pending {{/ if }}
      {{# if(promise.isRejected) }}
        {{ promise.reason.message }}
      {{/ if }}
      {{# if(promise.isResolved) }}
        {{ promise.value.message }}
      {{/ if}}
      
    • Stache has an {{else}} case for empty lists:
      {{# for( item of this.items ) }}
         <li>{{ item.name }}</li>
      {{ else }}
         <li>There are no items</li>
      {{/ }}
      

Basic Use

The following sections show you how to:

  • Load templates so they be processed into views.
  • Writing values within HTML to the page.
  • Writing some HTML to the page or some other HTML to the page with branch logic.
  • Loop over a list of values and writing some HTML out for each value.
  • Listen to events on elements.
  • Read and write to element properties and attributes.
  • Simplifying your templates with:
    • Variables
    • Helpers
    • Partials

Loading templates

There are several ways to load a stache template:

  • As a component's view.

    can-stache-element automatically processes strings passed to the view property as can-stache templates.

    <my-demo></my-demo>
    <script type="module">
    import {StacheElement} from "can";
    
    class MyDemo extends StacheElement {
      static view = `
        <h1>Hello {{ this.subject }}</h1>
      `;
    
      static props = {
        subject: "World"
      };
    };
    customElements.define("my-demo", MyDemo);
    </script>
    
  • Programmatically.

    Create a view function by importing stache and passing it a string.

    import {stache} from "can";
    
    // parses the template string and returns a view function:
    const view = stache(`<h1>Hello {{ this.subject }}</h1>`);
    
    // Calling the view function returns HTML elements:
    const documentFragment = view({subject: "World"});
    
    // Adds those elements to the page
    document.body.appendChild( documentFragment );
    
    console.log(document.body.innerHTML) //-> "<h1>Hello World</h1>";
    
  • Imported and pre-parsed.

    If you are using StealJS use steal-stache or if you are using webpack use can-stache-loader to create .stache file and import them like:

    import {StacheElement} from "can";
    import view from "./my-component.stache";
    
    static MyComponent extends StacheElement {
      static view = view;
      static props = { ... }
    }
    
    customElements.define("my-component", MyComponent);
    

Writing values

Use {{expression}} to write out values into the page. The following uses {{expression}} to write out the ViewModel's subject:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <h1>Hello {{ this.subject }}</h1>
    `;

    static props = {
        subject: "World"
    };
};
customElements.define("my-demo", MyDemo);
</script>

You can use {{expression}} on any part of an HTML element except the tag name:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <h1 class='{{this.className}}' {{this.otherAttributes}}>
            Hello {{ this.subject }}
        </h1>
    `;

    static props = {
        subject: "World",
        className: "bigger",
        otherAttributes: "id='123'"
    };
};
customElements.define("my-demo", MyDemo);
</script>

You can call methods within {{expression}} too:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <h1>Hello {{ this.caps( this.subject ) }}</h1>
    `;

    static props = {
        subject: "World"
    };

    caps(text) {
        return text.toUpperCase();
    }
};
customElements.define("my-demo", MyDemo);
</script>

{{expression}} will escape the value being inserted into the page. This is critical to avoiding cross-site scripting attacks. However, if you have HTML to insert and you know it is safe, you can use {{{expression}}} to insert it.

Branching Logic

Stache provides severals helpers that help render logic conditionally. For example, the following renders a sun if the time property equals "day":

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <p on:click="this.toggle()">
            Time:
            {{# eq(this.time,"day") }}
                SUN 🌞
            {{ else }}
                MOON 🌚
            {{/ eq }}
        </p>
    `;

    static props = {
        time: "day"
    };

    toggle() {
        this.time = (this.time === "day" ? "night" : "day");
    }
};
customElements.define("my-demo", MyDemo);
</script>

Notice that branching is performed using the {{#expression}}, {{else}} and {{/expression}} magic tags. These define "sections" of content to render depending on what the helper does. We call these the TRUTHY and FALSY sections. In the example above, the eq helper renders the TRUTHY section (SUN 🌞) if this.time equals "day". If this.time is not equal to "day", the FALSY section (MOON 🌚) is rendered.

The following helpers are used to render conditionally:

  • if - Renders the TRUTHY section if the value is truthy.
    EXAMPLE
    
  • not - Renders the TRUTHY section if the value is falsy.
  • eq - Renders the TRUTHY section all values are equal.
  • and - Renders the TRUTHY section if all values are truthy.
  • or - Renders the TRUTHY section if any value is truthy.
  • switch with case - Renders the case section that matches the value.
  • {{else}} - Renders the FALSY section if the value is falsy.

These helpers (except for switch) can be combined. For example, we can show the sun if this.time equals "day" or "afternoon" as follows:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <p on:click="this.toggle()">
            Time:
            {{# or( eq(this.time,"day"), eq(this.time, "afternoon") ) }}
                SUN 🌞
            {{ else }}
                MOON 🌚
            {{/ eq }}
        </p>
    `;

    static props = {
        time: "day"
    };

    toggle() {
        this.time = (this.time === "day" ? "night" :
            (this.time === "night" ? "afternoon" : "day"));
    }
};
customElements.define("my-demo", MyDemo);
</script>

NOTE: One of stache's goals is to keep your templates as simple as possible. It might be better to create a isSunUp method in the ViewModel and use that instead.

Looping

Use for(of) to loop through values. The following writes out the name of each todo:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <ul>
            {{# for(todo of this.todos) }}
                <li>{{ todo.name }}</li>
            {{/ for }}
        </ul>
    `;

    static props = {
        todos: {
            get default() {
                return [
                    {name: "Writing"},
                    {name: "Branching"},
                    {name: "Looping"}
                ]
            }
        }
    };
};
customElements.define("my-demo", MyDemo);
</script>

Use scope.index to access the index of a value in the array. The following writes out the index with each todo's name:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <ul>
            {{# for(todo of this.todos) }}
                <li>{{scope.index}} {{ todo.name }}</li>
            {{/ for }}
        </ul>
    `;

    static props = {
        todos: {
            get default() {
                return [
                    {name: "Writing"},
                    {name: "Branching"},
                    {name: "Looping"}
                ]
            }
        }
    };
};
customElements.define("my-demo", MyDemo);
</script>

Use for(of) to loop through key-value objects.

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <ul>
            {{# for(value of this.object) }}
                <li>{{scope.key}} {{ value }}</li>
            {{/ for }}
        </ul>
    `;

    static props = {
        object: {
            get default() {
                return {
                    first: "FIRST",
                    value: "VALUE"
                };
            }
        }
    };
};
customElements.define("my-demo", MyDemo);
</script>

Listening to events

on:event documents how you can listen to events on elements or props. The following listens to clicks on a button:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <button on:click="this.increment()">+1</button>
        Count: {{this.count}}
    `;

    static props = {
        count: 0
    };

    increment() {
        this.count++;
    }
};
customElements.define("my-demo", MyDemo);
</script>

Binding to properties and attributes

can-stache-bindings provides directional bindings to connect values in stache to element props or attributes.

This makes it easy to:

  • Write out property values.

    The following updates the checkboxes checked property if the status is not equal to 'critical':

    <my-demo></my-demo>
    <script type="module">
    import {StacheElement} from "can";
    
    class MyDemo extends StacheElement {
        static view = `
            <input type="checkbox"
                checked:from="not( eq(this.status, 'critical') )" />
                Can ignore?
    
            <button on:click="this.status = 'critical'">Critical</button>
            <button on:click="this.status = 'medium'">Medium</button>
            <button on:click="this.status = 'low'">Low</button>
        `;
    
        static props = {
            status: "low"
        };
    };
    customElements.define("my-demo", MyDemo);
    </script>
    
  • Update a value when an element property changes.

    The following updates the props name when the <input/> changes:

    <my-demo></my-demo>
    <script type="module">
    import {StacheElement} from "can";
    
    class MyDemo extends StacheElement {
        static view = `
            <input value:to="this.name" placeholder="name"/>
            Name: {{ this.name }}
        `;
    
        static props = {
            name: ""
        };
    };
    customElements.define("my-demo", MyDemo);
    </script>
    

can-stache-bindings supports a wide variety of different bindings. Please checkout its documentation.

Creating variables

The let helper lets you create local variables. For example, we can create a name variable and write to that:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        {{ let name='' }}
        <input value:to="name" placeholder="name"/>
        Name: {{ name }}
    `;
};
customElements.define("my-demo", MyDemo);
</script>

Variables can help you avoid unnecessary props like above. This is very handy when wiring StacheElements within a for(of) loop as follows:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        {{# for(todo of this.todos) }}
            {{ let locked=true }}
            <div>
                <p>
                    Locked:
                    <input type='checkbox' checked:bind="locked"/>
                </p>
                <p>
                    <input type='value' value:bind="todo.name" disabled:from="locked"/>
                </p>
            </div>
        {{/ for }}
    `;

    static props = {
        todos: {
            get default() {
                return [
                    {name: "Writing"},
                    {name: "Branching"},
                    {name: "Looping"}
                ];
            }
        }
    };
};
customElements.define("my-demo", MyDemo);
</script>

Currently, you can only create variables with let for the entire template or within for(of). If there are other blocks where you would find this useful, please let us know!

Creating helpers

Helpers can simplify your stache code. While CanJS comes with many helpers, adding your own can reduce code. There are several different types of helpers, each with different benefits.

Global Helpers

Use addHelper to create a helper function that can be called from every template. The following makes an upperCase helper:

<my-demo></my-demo>
<script type="module">
import {stache, StacheElement} from "can";

stache.addHelper("upperCase", function(value){
    return value.toUpperCase();
})

class MyDemo extends StacheElement {
    static view = `
        <h1>Hello {{ upperCase(this.subject) }}</h1>
    `;

    static props = {
        subject: "World"
    };
};
customElements.define("my-demo", MyDemo);
</script>

Global helpers are easy to create and understand, but they might create conflicts if another CanJS library defines a similar helper.

Component Methods

Instead of creating a global helper, add your helper functions on your component. The following adds the upperCase method to the component.

<my-demo></my-demo>
<script type="module">
import { stache, StacheElement, type } from "can";

class MyDemo extends StacheElement {
    static view = `
      <h1>Hello {{ this.upperCase(this.subject) }}</h1>
    `;

    static props = {
      subject: "World"
    };

    // View Helpers
    upperCase(value) {
      return value.toUpperCase();
    }
};
customElements.define("my-demo", MyDemo);
</script>

Importing Functions

If you are using a module loader to import stache files, can-view-import can be used to import a function to a let variable:

<can-import from="app/helpers/upperCase"  module.default:to="upperCase"/>
{{upperCase(name)}}

Creating partials

Partials are snippets of HTML that might be used several places. There are a few ways of reusing HTML.

Using Custom Elements

You can always define and use can-stache-element. The following defines and uses an <address-view> component:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class AddressView extends StacheElement {
    static view = `
        <address>{{this.street}}, {{this.city}}</address>
    `;
};

customElements.define("address-view", AddressView);

class MyDemo extends StacheElement {
    static view = `
        <h2>{{this.user1.name}}</h2>
        <address-view street:from="user1.street" city:from="user1.city"/>
        <h2>{{this.user2.name}}</h2>
        <address-view street:from="user2.street" city:from="user2.city"/>
    `;

    static props = {
        user1: {
            get default() {
                return {name: "Ramiya", street: "Stave", city: "Chicago"}
            }
        },
        user2: {
            get default() {
                return {name: "Bohdi", street: "State", city: "Chi-city"}
            }
        }
    };
};
customElements.define("my-demo", MyDemo);
</script>

Calling views

You can create views programmatically with stache, make those views available to another view (typically through the props). The following creates an addressView and makes it available to <my-demo>'s view through the addressView property on the ViewModel:

<my-demo></my-demo>
<script type="module">
import {stache, StacheElement} from "can";

const addressView = stache(`<address>{{this.street}}, {{this.city}}</address>`);

class MyDemo extends StacheElement {
    static view = `
        <h2>{{this.user1.name}}</h2>
        {{ addressView(street=user1.street city=user1.city) }}
        <h2>{{this.user2.name}}</h2>
        {{ addressView(street=user2.street city=user2.city) }}
    `;

    static props = {
      addressView: {
          get default() {
              return addressView;
          }
      },
      user1: {
          get default() {
              return {name: "Ramiya", street: "Stave", city: "Chicago"}
          }
      },
      user2: {
          get default() {
              return {name: "Bohdi", street: "State", city: "Chi-city"}
          }
      }
  };
};
customElements.define("my-demo", MyDemo);
</script>

Inline Partials

If a single template needs the same HTML multiple places, use {{<partialName}} to create an inline partial:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        {{< addressView }}
            <address>{{ this.street}}, {{ this.city }}</address>
        {{/ addressView }}
        <h2>{{ this.user1.name }}</h2>
        {{ addressView(user1) }}
        <h2>{{ this.user2.name }}</h2>
        {{ addressView(user2) }}
    `;

    static props = {
        user1: {
            get default() {
                return {name: "Ramiya", street: "Stave", city: "Chicago"}
            }
        },
        user2: {
            get default() {
                return {name: "Bohdi", street: "State", city: "Chi-city"}
            }
        }
    };
};
customElements.define("my-demo", MyDemo);
</script>

Other uses

Reading promises

Stache can read "virtual" properties from Promises and other types configured to work with getKeyValue.

The following "virtual" keys can be read from promises:

  • isPending - true if the promise has not been resolved or rejected.
  • isResolved - true if the promise has resolved.
  • isRejected - true if the promise was rejected.
  • value - the resolved value.
  • reason - the rejected value.
<my-demo></my-demo>
<script type="module">
import { StacheElement, type } from "can";

class MyDemo extends StacheElement {
    static view = `
        <div>
            {{# if(promise.isPending) }} Pending... {{/ if }}
            {{# if(promise.isRejected) }}
                Rejected! {{ promise.reason }}
            {{/ if }}
            {{# if(promise.isResolved) }}
                Resolved: {{ promise.value }}
            {{/ if}}
        </div>
        <button on:click="resolve('RESOLVED',2000)">Resolve in 2s</button>
        <button on:click="reject('REJECTED',2000)">Reject in 2s</button>
    `;

    static props = {
        promise: type.Any
    };

    resolve(value, time) {
        this.promise = new Promise((resolve)=>{
            setTimeout(()=>{
                resolve(value);
            },time)
        });
    }

    reject(value, time) {
        this.promise = new Promise((resolve, reject)=>{
            setTimeout(()=>{
                reject(value);
            },time)
        });
    }

    connected() {
        this.resolve("RESOLVED", 2000);
    }
};
customElements.define("my-demo", MyDemo);
</script>

Animation

Use on:event to listen to an event and call an animation library.

The following listens to when a todo's complete event is fired and calls this.shake. this.shake uses anime to animate the <div>:

<my-demo></my-demo>
<script src="//cdnjs.cloudflare.com/ajax/libs/animejs/2.0.2/anime.min.js"></script>
<script type="module">
import {ObservableObject, StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        {{# for(todo of todos) }}
            <div on:complete:by:todo="this.shake(scope.element)">
                <input type="checkbox" checked:bind="todo.complete"/>
                {{todo.name}}
            </div>
        {{/ for }}
    `;

    static props = {
        todos: {
            get default() {
                return [
                    new ObservableObject({name: "animate", complete: false}),
                    new ObservableObject({name: "celebrate", complete: true})
                ];
            }
        }
    };

    shake(element) {
        anime({
            targets: element,
            translateX: [ 10,-10,0 ],
            easing: 'linear'
        });
    }
};
customElements.define("my-demo", MyDemo);
</script>

Syntax Highlighting

Stache is very similar to handlebars and mustache. Most editors have plugins for one of these formats.

Spacing and formatting

Stache tolerates spacing similar to JavaScript. However, we try to following the following spacing in the following example:

{{# if( this.check ) }}
    {{ this.value }}
{{ else }}
    {{ this.method( arg1 ) }}
{{/ if}}

You can use the following regular expressions to create this spacing:

  • replace \{\{([^ #\/\^!]) with {{ $1
  • replace \{\{([#\/\^!])([^ ]) with {{$1 $2
  • replace ([^ ])\}\} with $1 }}

Accessing a helper if your property overwrites

Sometimes you have data with properties that conflict with stache's helpers, but you still need to access those helpers. To do this, you can access all those helpers on scope.helpers like scope.helpers.eq.

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <p on:click="this.toggle()">
            Time:
            {{# scope.helpers.eq(this.eq,"day") }}
                SUN 🌞
            {{ else }}
                MOON 🌚
            {{/ }}
        </p>
    `;

    static props = {
        eq: "day"
    };

    toggle() {
        this.eq = (this.eq === "day" ? "night" : "day");
    }
};
customElements.define("my-demo", MyDemo);
</script>

Removing whitespace

Stache renders whitespace. For example, the following will render the space between the <h1> tags and the {{this.message}} magic tag:

import {stache} from "can";

var view = stache(`<h1>
    {{this.message}}
</h1>`);

var fragment = view({message: "Hi"});

console.log( fragment.firstChild.innerHTML ) //-> "\n\tHi\n"

You can use {{-expression-}} to remove this whitespace like:

import {stache} from "can";

var view = stache(`<h1>
    {{-this.message-}}
</h1>`);

var fragment = view({message: "Hi"});

console.log( fragment.firstChild.innerHTML ) //-> "Hi"

Understanding the stache language

Stache has a variety of magic tags and expressions that control the behavior of the DOM it produces. Furthermore, you are able to customize this behavior to a large extent.

The following sections outline stache's formal syntax and grammar. This knowledge can be useful when attempting to combine features into advanced functionality.

  • Magic tags - Magic tags like {{expression}} and {{{expression}}} control control how stache operates on the DOM.
  • Expression types - This is the valid semantics within a magic tag. For example, you can call functions like {{ this.callSomeMethod() }}.
  • Scope and context - How variables and this get looked up.

Magic tags

Rendering behavior is controlled with magic tags that look like {{}}. There are several forms of magic tags:

  • Insertion tags
    • {{expression}} - Insert escaped content into the DOM.
    • {{{expression}}} - Insert unescaped content into the DOM.
    • {{!expression}} - Make a comment.
  • Section tags - optional render a sub-section.
    • {{#expression}}TRUTHY{{else}}FALSY{{/expression}} - Optionally render the TRUTHY or FALSY section.
    • {{^expression}}FALSY{{else}}TRUTHY{{/expression}} - Optionally render the TRUTHY or FALSY section.
  • Special
    • {{<partialName}}...{{/partialName}} - Create an inline partial.
    • {{-expression-}} - Remove whitespace.

Magic tags are valid in the following places in HTML:

  • Between a open and closed tag:
    <div> {{magic}} </div>
    <div> {{#magic}} {{/magic}} </div>
    
  • Wrapping a series of opening and closing tags:
    <div> {{#magic}} <label></label> {{/magic}} </div>
    <div> {{#magic}} <label></label><span></span> {{/magic}} </div>
    
  • Within an attribute:
    <div class="selected {{magic}}"></div>
    <div class="{{#magic}}selected{{/magic}}"></div>
    
  • Within a tag:
    <div {{magic}}></div>
    
  • Within a tag, wrapping attributes:
    <div {{#magic}}class="selected"{{/magic}}></div>
    <input {{#magic}}checked{{/magic}}></div>
    

The following places are not supported:

  • Defining the tag name:
    <{{tagName}}></{{tagName}}>
    
  • Wrapping an opening or closing tag:
    <div> {{#magic}} <label> {{/magic}} </label></div>
    <div> <label> {{#magic}} </label><span></span> {{/magic}} </div>
    
  • Intersecting part of an attribute:
    <div {{attributeName}}="selected"></div>
    <div {{#magic}}class="{{/magic}}selected"></div>
    
  • Attribute values without quotes:
    <div attribute={{#magic}}"foo"{{/magic}}></div>
    <div key:raw={{#magic}}"foo"{{/magic}}></div>
    <div key:from={{#magic}}{{foo}}{{/magic}}></div>
    

Expression types

Stache supports different expression types within most of the magic tags. The following uses most of the expressions available:

<div> {{ this.method( 1, keyA=null keyB=true )[key]( "string", value ) }}

There are 6 expression types stache supports:

  • Literal expressions like {{"string"}}
  • KeyLookup expressions like {{key}}
  • Call expressions like {{method(arg)}}
  • Hash expressions like {{prop=key}}
  • Bracket expressions like {{[key]}}
  • Helper expressions like {{helper arg}} (deprecated, but will probably be supported forever)

Literal expressions

A Literal Expression specifies JS primitive values like:

  • Strings "strings"
  • Numbers 5
  • Booleans true or false
  • And null or undefined

They are usually passed as arguments to Call expressions like:

{{ task.filter( "completed", true ) }}

KeyLookup expressions

A KeyLookup Expression specifies a value in the scope that will be looked up. KeyLookup expressions can be the entire stache expression like:

{{ key }}

Or they can make up the method, arguments, bracket, and hash value parts of Call and Hash expressions:

{{ method( arg1, arg2 ) }}      Call
{{ method( prop=hashValue ) }}  Hash
{{ [key] }}                     Bracket

The value returned up by a KeyLookup depends on what the key looks like, and the scope.

Call expressions

A Call Expression calls a function looked up in the scope. It looks like:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <h1>{{ this.pluralize(this.type, this.ages.length) }}</h1>
    `;

    static props = {
        ages: {
            get default() {
                return [ 22, 32, 42 ];
            }
        },

        type: "age"
    };

    pluralize(type, count) {
        return type + ( count === 1 ? "" : "s" );
    }
};
customElements.define("my-demo", MyDemo);
</script>

Call expression arguments are comma (,) separated. If a Hash expression is an argument, an object with the hash properties and values will be passed. For example:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <h1>{{ this.pluralize(word=this.type count=this.ages.length) }}</h1>
    `;

    static props = {
        ages: {
            get default() {
                return [ 22, 32, 42 ];
            }
        },

        type: "age"
    };

    pluralize(options) {
        return options.word + ( options.count === 1 ? "" : "s" );
    }
};
customElements.define("my-demo", MyDemo);
</script>

Hash expressions

A Hash Expression specifies a property value on a object argument. Notice how method is called below:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <h1>{{ this.method(a=this.aProp b=null, c=this.func() ) }}</h1>
    `;

    static props = {
        aProp: "aValue"
    };

    method(arg1, arg2) {
        console.log(arg1, arg2) //-> {aProp: "aValue", b: null},{c:"FUNC"}
    }

    func() {
        return "FUNC";
    }
};
customElements.define("my-demo", MyDemo);
</script>

Bracket expressions

A Bracket Expression can be used to look up a dynamic property in the scope. This is very useful when looping through properties to write out on many records:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <table>
            {{# for(record of records) }}
                <tr>
                    {{# for(key of keys )}}
                        <td>{{ record[key] }}</td>
                    {{/ for}}
                </tr>
            {{/ for}}
        </table>
    `;

    static props = {
        records: {
            get default() {
                return [
                    {first: "Justin", last: "Meyer", label: "Dad"},
                    {first: "Payal", last: "Meyer", label: "Mom"},
                    {first: "Ramiya", last: "Meyer", label: "Babu"},
                    {first: "Bohdi", last: "Meyer", label: "Baby"}
                ];
            }
        },
        keys: {
            get default() {
                return [
                    "first","last","label"
                ];
            }
        }
    };
};
customElements.define("my-demo", MyDemo);
</script>

This can be useful for looking up values using keys containing non-alphabetic characters:

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <h1>{{ this.data["special:prop"] }}</h1>
    `;

    static props = {
        data: {
            get default() {
                return {"special:prop": "SPECIAL VALUE"}
            }
        }
    };
};
customElements.define("my-demo", MyDemo);
</script>

Bracket expressions can also be used to look up a value in the result of another expression:

{{ this.getPerson()[key] }}

Helper expressions

Helper Expressions are supported but deprecated. It's unlikely they will be dropped for a long time.

Scope and context

Stache maintains a scope similar to the one maintained in JavaScript. For example, the inner function is able to access the message, last, and first variables:

const message = "Hello";
function outer() {
    const last = "Meyer";

    function inner() {
        const first = "Bohdi";
        console.log( message + " " + first + " " + last );
    }
    inner();
}
outer();

Stache was originally built with a handlebars and mustache-type scope. This scope is still supported, but deprecated. If you are supporting templates in this style, please read Legacy Scope Behavior.

The modern style of stache works much more like JavaScript. A view is rendered with a context accessible as this. For example:

import {stache} from "can";
var view = stache(`<h1>Hello {{ this.subject }}</h1>`);

var context = {
    message: "World"
};

var fragment = view(context);

console.log(fragment.firstChild.innerHTML)
//-> Hello World

The for(of) helper creates variables local to the section. In the following example todo is only available between {{# for(...)}} and {{/ for }}.

<my-demo></my-demo>
<script type="module">
import {StacheElement} from "can";

class MyDemo extends StacheElement {
    static view = `
        <ul>
            {{# for(todo of this.todos) }}
                <li>{{ todo.name }}</li>
            {{/ for }}
        </ul>
    `;

    static props = {
        todos: {
            get default() {
                return [
                    {name: "Writing"},
                    {name: "Branching"},
                    {name: "Looping"}
                ];
            }
        }
    };
};
customElements.define("my-demo", MyDemo);
</script>

When a variable like todo is looked up, it will look for variables in its scope and then walk to parent scopes until it finds a value.

See also

can-view-scope is used by stache internally to hold and lookup values. This is similar to how JavaScript’s closures hold variables, except you can use it programmatically.

can-stache-element and can-view-callbacks.tag allow you to define custom elements for use within a stache template. can-view-callbacks.attr allow you to define custom attributes.

can-stache-bindings sets up element and bindings between a stache template’s can-view-scope, component props, or an element’s attributes.

How it works

Coming soon!

CanJS is part of DoneJS. Created and maintained by the core DoneJS team and Bitovi. Currently 6.0.1.

On this page

Get help

  • Chat with us
  • File an issue
  • Ask questions
  • Read latest news