can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
673 lines (511 loc) • 21.8 kB
HTML
<!--####################################################################
THIS IS A GENERATED FILE -- ANY CHANGES MADE WILL BE OVERWRITTEN
INSTEAD CHANGE:
source: [object Object]
@page guides/pmo/StacheTemplates
######################################################################## -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>CanJS - Stache Templates</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="../../static/bundles/bit-docs-site/static.css">
<link rel="shortcut icon" sizes="16x16 24x24 32x32 48x48 64x64" href="/docs/images/canjs_favicon.ico">
<link rel="apple-touch-icon" sizes="57x57" href="../../../docs/images/canjs_favicon_57x57.png">
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="../../../docs/images/canjs_favicon_57x57.png">
<link rel="apple-touch-icon" sizes="72x72" href="../../../docs/images/canjs_favicon_72x72.png">
<link rel="apple-touch-icon" sizes="114x114" href="../../../docs/images/canjs_favicon_114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="../../../docs/images/canjs_favicon_128x128.png">
<link rel="apple-touch-icon" sizes="144x144" href="../../../docs/images/canjs_favicon_144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="../../../docs/images/canjs_favicon_152x152.png">
<meta content="yes" name="apple-mobile-web-app-capable">
<meta name="apple-mobile-web-app-status-bar-style" content="white-translucent">
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-2302003-11', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger"/>
<label for="nav-trigger">Menu</label>
<div id="everything">
<div id="left" class="column">
<div class="top-left">
<div class="brand">
<div class="logo">
<a href="../../../index.html" alt="CanJS"></a>
<div class="dropdown project-dropdown">
<a href="https://donejs.com/">DoneJS</a>
<a href="http://stealjs.com/">StealJS</a>
<a href="http://jquerypp.com/">jQuery ++</a>
<a href="https://funcunit.com/">FuncUnit</a>
<a href="http://documentjs.com/">DocumentJS</a>
</div>
</div>
<div class="version">
<div class="version-number">
3.0.0
</div>
<div class="dropdown version-dropdown">
<a href="https://v2.canjs.com">2.3.27</a>
</div>
</div>
</div>
<div class="search-bar">
<p>
</p>
</div>
</div>
<div class="bottom-left">
<div class="social-side-container">
<ul class="social-side">
<li>
<a class="header-mobile github" href="https://github.com/canjs/canjs" target="_blank"><img class="social-icon-small" src="../../../docs/images/github.png">Github</a>
</li>
<li>
<a class="header-mobile twitter" href="https://twitter.com/canjs" target="_blank"><img class="social-icon-small" src="../../../docs/images/twitter.png">Twitter</a>
</li>
</ul>
<ul class="social-side">
<li>
<a class="header-mobile" href="https://gitter.im/canjs/canjs" target="_blank">Chat</a>
</li>
<li>
<a class="header-mobile" href="http://forums.donejs.com/c/canjs" target="_blank">Forum</a>
</li>
</ul>
</div>
<ul>
<li class="
">
<a class="page"
href="Components.html"
title="">
Components
</a>
</li>
<li class="
">
<a class="page"
href="ApplicationDesign.html"
title="">
Application Design
</a>
</li>
<li class="
">
<a class="page"
href="../../pmo/Setup.html"
title="">
Setup
</a>
</li>
<li class="
">
<a class="page"
href="Constructors.html"
title="">
Constructors
</a>
</li>
<li class="
">
<a class="page"
href="TheDefinePlugin.html"
title="">
The Define Plugin
</a>
</li>
<li class="current
parent
expanded">
<a class="page"
href="StacheTemplates.html"
title="">
Stache Templates
</a>
</li>
<li class="
">
<a class="page"
href="AppStateAndRouting.html"
title="">
App State and Routing
</a>
</li>
<li class="
">
<a class="page"
href="Observables.html"
title="">
Observables
</a>
</li>
<li class="
">
<a class="page"
href="ViewModels.html"
title="">
View Models
</a>
</li>
<li class="
">
<a class="page"
href="DataModelsAndFixtures.html"
title="">
Data Models and Fixtures
</a>
</li>
<li class="
">
<a class="page"
href="LoadingStates.html"
title="">
Loading States
</a>
</li>
<li class="
">
<a class="page"
href="EventHandling.html"
title="">
Event Handling
</a>
</li>
<li class="
">
<a class="page"
href="WebServiceCommunication.html"
title="">
Web Service Communication
</a>
</li>
<li class="
">
<a class="page"
href="Recap.html"
title="">
Recap
</a>
</li>
</ul>
</div>
</div>
<div id="right" class="column">
<div class="top-right">
<div class="top-right-top">
<ul class="top-right-bitovi">
<li class="dropdown">
<a href="http://bitovi.com" class="bitovi icon-bits">Bitovi</a>
<ul class="dropdown-menu">
<li><a href="http://bitovi.com">Bitovi.com</a></li>
<li><a href="http://bitovi.com/blog/">Blog</a></li>
<li><a href="http://bitovi.com/consulting/">Consulting</a></li>
<li><a href="http://bitovi.com/training/">Training</a></li>
<li><a href="http://bitovi.com/open-source/">Open Source</a></li>
</ul>
</li>
</ul>
<div class="brand">
<div class="logo">
<a href="../../../index.html" alt="CanJS"></a>
</div>
</div>
<ul class="top-right-links">
<li>
<a href="https://gitter.im/canjs/canjs">Chat</a>
</li>
<li>
<a href="http://forums.donejs.com/c/canjs">Forum</a>
</li>
<li>
<a class="github-button nav-social" href="https://github.com/canjs/canjs" data-count-href="/canjs/canjs/stargazers" data-count-api="/repos/canjs/canjs#stargazers_count">Star</a>
</li>
<li>
<a href="https://twitter.com/canjs" class="twitter-follow-button nav-social" data-show-count="true" data-show-screen-name="false">Follow @canjs</a><script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
</li>
</ul>
</div>
<div class="breadcrumb">
<li><a href="../pmo.html">Place My Order Guide</a></li> /
<li><a href="StacheTemplates.html">Stache Templates</a></li>
<li class="breadcrumb-dropdown">/ <a> On this page</a>
<ul class="on-this-page"></ul>
</li>
<div class="nav-toggle" title="Back to top"></div>
</div>
</div>
<div class="bottom-right">
<article>
<section class="title">
<div class="page-type">
<h1>Stache Templates</h1>
<div>page</div>
</div>
<section class="description">
</section>
</section>
<section class="on-this-page-table">
</section>
<section class="title-footer">
<ul class="title-links">
<!-- <li><a href="#">docco</a></li> -->
<li><a href="//github.com/canjs/canjs/tree/v3.0.0/docs/can-guides/experiment/pmo/stache-templates.md">source</a></li>
<!-- <li><a href="#">download</a></li> -->
<!-- <li><a href="#">tests</a></li> -->
</ul>
</section>
<section class="body">
<div class="getting-started">
<hr />
<p><strong>In this Chapter</strong></p>
<ul>
<li>Stache Templates</li>
</ul>
<p>Get the code for: <a href="/guides/examples/PlaceMyOrder/ch-2_canjs-getting-started.zip">chapter: stache templates</a></p>
<hr />
<p>Now that we have a basic sketch of our application, and we've covered a few CanJS fundamentals,
it’s time for us to start working with the sample application. We'll begin with the
application's templates.</p>
<p>As mentioned in the <a href="./Tutorial.html">introduction</a>, we’re using Stache templates in
our app. Remember that when we downloaded our custom build of CanJS, we
included the <a href="../docs/can.stache.html">can.stache</a> plugin.</p>
<p>We can create a simple template, render it to a document fragment and insert it into the page like
the following:</p>
<pre><code>var template = can.stache("<h1>{{message}}</h1>");
var frag = template({message: "Hello World"});
$("body").append(frag);
</code></pre>
<p>Stache templates look similar to normal HTML, except they contain magic tags that contain
a very simple language that can be used to:</p>
<ul>
<li><a href="#context">lookup and insert values into the html output</a></li>
<li><a href="#enumeration">loop over arrays and can.Lists</a></li>
<li><a href="#conditionallogic">control-flow behavior like if and switch</a></li>
<li><a href="#partials">render other templates with partials</a></li>
<li>perform custom control-flow behavior</li>
</ul>
<p>Stache templates support both <a href="https://github.com/janl/mustache.js/">Mustache</a>
and <a href="http://handlebarsjs.com/">Handlebar</a> syntax. For more
information on the details of these formats, see their respective websites.</p>
<p>It will be easiest for us to look at these with an example, so let’s work with
one. Open up your <code>components/order_details/order_details.stache</code> file.
It should look like this:</p>
<pre><code class="language-html">{{#with order}}
<h3>Thanks for your order {{name}}!</h3>
<div>
<label class="control-label">Confirmation Number: {{_id}}</label>
</div>
<h4>Items ordered:</h4>
<ul class="list-group panel">
{{#each items}}
<li class="list-group-item">
<label>
{{name}} <span class="badge">${{price}}</span>
</label>
</li>
{{/each}}
<li class="list-group-item">
<label>Total <span class="badge">${{total}}</span></label>
</li>
</ul>
<div><label class="control-label">Phone: {{phone}}</label></div>
<div><label class="control-label">Address: {{address}}</label></div>
{{/with}}
</code></pre>
<p><a name="context"></a></p>
<h2>Value lookup</h2>
<p>Assume for the moment that we have the following <code>customerOrder</code> map passed in to our Stache template:</p>
<pre><code>{
customerNumber: 12543,
customerType: 'Business',
order: {
name: 'Rudloph Steiner',
_id: 837267,
items: [
{
name: 'Garden Gnome',
price: 23.70
}
],
total: 23.70,
phone: '+49 170 345 6789',
address: 'Beuselstrasse 15, Berlin',
total: function(){
var sum = 0;
this.items.forEach(function(item){
sum += item.price;
});
return sum;
}
}
}
</code></pre>
<p>If we want to show the <code>customerType</code> in a <code><span></code> we can do that in a stache template like the following:</p>
<pre><code><span>{{customerType}}</span>
</code></pre>
<p>What's inside the magic tags, in this case <code>customerType</code>, is a
<a href="../docs/can.stache.expressions.html#section_KeyLookupexpressions">key lookup expression</a>.
<a href="../docs/can.stache.key.html">Keys</a> are used to lookup values in the
<a href="../docs/can.view.Scope.html">template scope</a>.</p>
<p>A DOT(<code>.</code>) operator
can be used to lookup nested values. For example:</p>
<pre><code><h3>Thanks for your order {{order.name}}!</h3>
<span>{{customerType}}</span>
</code></pre>
<p>Similar to variable lookup JavaScript, a stache key lookup can search for a value in multiple places.
Each of these places is called a <strong>context</strong>. The collection of all available contexts for a key lookup
is called a <a href="../docs/can.view.Scope.html">scope</a>.</p>
<p>The root context is
the data passed to a template. In this case, the root context is the <code>customerOrder</code>
object at the begining of this section. This is why <code>{{customerType}}</code> outputs <code>Business</code>.</p>
<p><a href="../docs/can.stache.tags.section.html">Sections</a>
create contexts in Stache. A section begins with <code>{{#EXPRESSION}}</code> or <code>{{^EXPRESSION}}</code>
and ends with <code>{{/EXPRESSION}}</code>. In the following example <code>{{#with order}}</code>
defines a section whose scope lookup starts finding values in the
<code>customerOrder</code>'s <code>order</code> object first:</p>
<pre><code class="language-html">{{#with order}}
<h3>Thanks for your order {{name}}!</h3>
<span>{{customerType}}</span>
{{#with order}}
</code></pre>
<p>In between <code>{{#with order}}</code> and <code>{{/with}}</code>, the scope's contexts look like:</p>
<pre><code>[
customerOrder.order,
customerOrder
]
</code></pre>
<p>The top of the scope is called the <strong>current context</strong>. In this case it is <code>customerOrder.order</code>.</p>
<p>When <code>{{name}}</code> is looked up, it will first look for <code>name</code> on the <strong>current context</strong>. As that value
exists, <code>Rudloph Steiner</code> will be returned.</p>
<p>When <code>{{customerType}}</code> is looked up, it will look for <code>customerType</code> on the <strong>current context</strong>. As
that value does not exist, the next context, <code>customerOrder</code>, will be searched. The value of
<code>customerOrder.customerType</code> will be returned.</p>
<p>From within a given scope, you can reference the <strong>current context</strong> or control which context
should be used to find values.</p>
<p>See examples below:</p>
<pre><code class="language-html">{{#with order}}
<div>My Current Context Object: {{.}}</div> <!-- references the order object-->
<div>My Parent Context Object: {{../.}}</div> <!-- references the customerOrder object-->
<div>An Item on my Parent Context's Object: {{../customerNumber}}</div>
<div>My Parent's Parent Context Object: {{../../.}}</div> <!-- example of how you might access the parent of a parent -->
{{/with}}
</code></pre>
<p><a name="enumeration"></a></p>
<h2>Looping over arrays</h2>
<p>Enumerating allows you to loop through the contents of an iterable item. We’ve done this above for
the options in our select dropdown. The <code>{{#each key}} ... {{/each}}</code> tag set
is used to iterate over an enumerable collection, such as an array. In the
example above, we are looping over an array of objects. As with <a href="#context">sections</a>,
the properties of the objects we are iterating over are accessible
from data keys inside the <code>#each</code> scope without dot notation. In the example
above, we saw:</p>
<pre><code class="language-html">{{#each items}}
<li class="list-group-item">
<label>
{{name}} <span class="badge">${{price}}</span>
</label>
</li>
{{/each}}
</code></pre>
<p>Because the context of the <code>{{#each}}</code> block is <code>items</code>, we can reference
the <code>name</code> and <code>price</code> properties of <code>items</code> directly—i.e, we don't need to
write <code>{{items.name}}</code> or <code>{{items.price}}</code>, we can just write <code>{{name}}</code> or <code>{{price}}</code>.</p>
<p>Call expressions can also be passed
to <a href="../docs/can.stache.helpers.each.html">#each</a>. For example, a ViewModel might
have a method to get menu items for a particular menu like <code>"dinner"</code> or
<code>"lunch"</code> like:</p>
<pre><code>var OrderViewModel = can.Map.extend({
itemsForMenuType: function(type){
return this.attr("menu.items").filter(function(item){
return item.attr("type") === type;
})
}
})
</code></pre>
<p>Call this method and return its result to <code>#each</code> like:</p>
<pre><code>{{#each itemsForMenuType("lunch")}}
<li>...</li>
{{/each}}
</code></pre>
<p>Note that <a href="../docs/can.stache.tags.section.html">#key</a> can also
be used to loop through objects with enumerable properties. In general,
<a href="../docs/can.stache.helpers.each.html">#each</a> should be used if the key references
<a href="../docs/can.List.html">can.List</a> or Arrays that have or often have incremental updates. <a href="../docs/can.stache.tags.section.html">#key</a>
should be used when the list is replaced by a list with items that look
nothing like the previous list's items.</p>
<p><a name="conditionallogic"></a></p>
<h2>Conditional Logic</h2>
<p>Stache templates have a limited capacity for conditional logic. Open up your
<code>main.stache</code> file. It should look like this:</p>
<pre><code>{{> header.stache}}
{{#eq page "home"}}
{{> home.stache}}
{{/eq}}
{{#eq page "restaurants"}}
{{#if slug}}
{{#eq action 'order'}}
<pmo-order {(slug)}="slug"></pmo-order>
{{/eq}}
{{^if action}}
<pmo-restaurant-details {(slug)}="slug"></pmo-restaurant-details>
{{/if}}
{{else}}
<pmo-restaurant-list></pmo-restaurant-list>
{{/if}}
{{/eq}}
{{#eq page "orders"}}
<pmo-order-history></pmo-order-history>
{{/eq}}
</code></pre>
<p>You’ll see two different helpers: <code>eq</code> and <code>if</code>. The <code>eq</code> helper takes two
arguments: the first being the key that is within the current section, and
the second a value to compare to the first argument to see if they are equal.
The <code>if</code> helper checks for one truthy argument before rendering what the
<code>if</code> block contains.</p>
<p>You might also notice the use of the <code>^</code> character, which will render the
section if the result of the helper is false. In other words, you can write
<code>{{^if action}}content{{/if}}</code> instead of <code>{{#if action}}{{else}}content{{/if}}</code></p>
<p>In general, it’s best to keep complex logic out of your templates. Their main function
should be to display data from the view model. If you need to use more complex logic
to display data in your templates, you can use a helper. Helpers are not covered in detail
in this guide; but you can get more information on them in the API: <a href="../docs/can.Component.prototype.helpers.html">Helpers</a></p>
<p><a name="partials"></a></p>
<h2>Partials</h2>
<p>You can nest templates in other templates by using partials. Partials inherit
the context from which 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 <code>{{> }}</code>.</p>
<p>In our example above, you can see that <code>{{> header.stache}}</code> includes the
<code>header.stache</code> file into the template.</p>
<hr />
<p><span class="pull-left"><a href="TheDefinePlugin.html">‹ The Define Plugin</a></span>
<span class="pull-right"><a href="AppStateAndRouting.html">App State and Routing ›</a></span></p>
</div>
</section>
<script type="text/javascript">
var docObject = {"src":{"path":"docs/can-guides/experiment/pmo/stache-templates.md"},"description":"\n","name":"guides/pmo/StacheTemplates","title":"Stache Templates","type":"page","parent":"guides/pmo","order":5,"disabletableofcontents":true,"comment":" ","pathToRoot":"../../.."};
</script>
</article>
<footer><p>CanJS is part of <a href="http://donejs.com" target="_blank">DoneJS</a>. Created and maintained by the core <a href="https://donejs.com/About.html#section=section_Team" target="_blank">DoneJS team</a> and <a href="http://bitovi.com" target="_blank">Bitovi</a>. <strong>Currently 3.0.0.</strong></p></footer>
</div>
</div>
</div>
<script>
steal = {
instantiated: {
"bundles/bit-docs-site/static.css!$css" : null
}
};
</script>
<script type='text/javascript' data-main="bit-docs-site/static" src="../../static/node_modules/steal/steal.production.js"></script>
<script async defer src="https://buttons.github.io/buttons.js"></script>
</body>
</html>