can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
631 lines (473 loc) • 19.1 kB
HTML
<!--####################################################################
THIS IS A GENERATED FILE -- ANY CHANGES MADE WILL BE OVERWRITTEN
INSTEAD CHANGE:
source: [object Object]
@page guides/pmo
######################################################################## -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>CanJS - Place My Order Guide</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="pmo/Components.html"
title="">
Components
</a>
</li>
<li class="
">
<a class="page"
href="pmo/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="pmo/Constructors.html"
title="">
Constructors
</a>
</li>
<li class="
">
<a class="page"
href="pmo/TheDefinePlugin.html"
title="">
The Define Plugin
</a>
</li>
<li class="
">
<a class="page"
href="pmo/StacheTemplates.html"
title="">
Stache Templates
</a>
</li>
<li class="
">
<a class="page"
href="pmo/AppStateAndRouting.html"
title="">
App State and Routing
</a>
</li>
<li class="
">
<a class="page"
href="pmo/Observables.html"
title="">
Observables
</a>
</li>
<li class="
">
<a class="page"
href="pmo/ViewModels.html"
title="">
View Models
</a>
</li>
<li class="
">
<a class="page"
href="pmo/DataModelsAndFixtures.html"
title="">
Data Models and Fixtures
</a>
</li>
<li class="
">
<a class="page"
href="pmo/LoadingStates.html"
title="">
Loading States
</a>
</li>
<li class="
">
<a class="page"
href="pmo/EventHandling.html"
title="">
Event Handling
</a>
</li>
<li class="
">
<a class="page"
href="pmo/WebServiceCommunication.html"
title="">
Web Service Communication
</a>
</li>
<li class="
">
<a class="page"
href="pmo/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 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>Place My Order Guide</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/tutorial.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">
<p><em>Better Apps, Faster</em></p>
<p>CanJS is a lightweight, modern JavaScript <a href="https://en.wikipedia.org/wiki/Model_View_ViewModel" target="_blank">MVVM</a>
framework that’s fast and easy to use, while remaining robust and extensible
enough to power some of the most trafficked websites in the world. This guide
will walk you through an analysis of a small e-commerce app built with CanJS called <strong>Place My Order</strong>.
In each relevant section, we’ll give you some code to play with
so you will have hands on experience working with CanJS.</p>
<p><img src="../can/guides/images/application-design/Home.png" alt="place-my-order.com home page" /></p>
<p>For a version of this guide that walks through testing, documenting, building, and deploying the same
application, checkout <a href="http://donejs.com/place-my-order.html">DoneJS's In Depth Guide</a>. This
guide focuses more on the CanJS parts.</p>
<h2>The Basics</h2>
<p>Every CanJS application contains:</p>
<ul>
<li><a href="#observables">Observables</a>,</li>
<li><a href="#models">Models</a>,</li>
<li><a href="#view-models">ViewModels</a></li>
<li><a href="#views">Views</a>,</li>
<li><a href="#custom_elements">Custom Elements</a>, and</li>
<li><a href="#routing">Routing with an AppViewModel</a></li>
</ul>
<p><a name="observables"></a></p>
<h3>Observables</h3>
<p>Observable objects provide a way for you to make changes to data and listen to
those changes. Observables such as <a href="../docs/can.List.html">can.List</a>, <a href="../docs/can.Map.html">can.Map</a>, and
<a href="../docs/can.compute.html">can.compute</a> provide the
foundation for models, view-models, view bindings, and even routing in your app. <a href="../docs/can.compute.html">can.compute</a>
is able to combine observable values into new observable values.</p>
<p><a href="http://justinbmeyer.jsbin.com/koqaxe/edit?js,console">Example: Creating a derived value from source observables.</a></p>
<pre><code>var info = can.compute(function(){
return person.attr("first")+" "+person.attr("last")+
" likes "+ hobbies.join(", ")+".";
});
</code></pre>
<p>The <a href="../docs/can.Map.prototype.define.html">define plugin</a> allows you to define rich property behaviors on
custom Map types.</p>
<p><a href="http://justinbmeyer.jsbin.com/wuwifaf/edit?js,console">Example: Creating a derived value as part of a custom type.</a></p>
<pre><code>Person = can.Map.extend({
define: {
fullName: {
get: function(){
return this.attr("first")+" "+this.attr("last");
}
}
}
});
</code></pre>
<p><a name="models"></a></p>
<h3>Models</h3>
<p>Models let you get and modify data from the server. They also hydrate
raw, serialized service data into more useful (and observable) typed
data in the client. <a href="../docs/can.Model.html">can.Model</a> makes it easy to connect to restful services
and perform Create, Retrieve, Update, and Delete (CRUD) operations.</p>
<p>For applications requiring real-time, high performance, restful data connections you should check out <a href="http://connect.canjs.com/">can-connect</a>.</p>
<p><a href="http://justinbmeyer.jsbin.com/codubev/edit?js,console">Example: Simulate a restful service and create, update, and delete its data.</a></p>
<pre><code>// Create an order.
var order = new Order({
price: 20
});
// Create it on the server.
order.save().then(function(order){
// Change its values and
// update it on the server.
return order.attr("price",22)
.save();
}).then(function(order){
// Destroy it on the server.
return order.destroy();
});
</code></pre>
<p><a name="view-models"></a></p>
<h3>ViewModels</h3>
<p>ViewModels contain the state and model data used by views to create HTML. They also
contain methods that the views can call. Custom <a href="../docs/can.Map.html">can.Map</a> types
are used as easily unit-testable view-models.</p>
<p><a href="http://jsbin.com/sotero/edit?js,output">Example: Define and test a view-model that derives values from source state.</a></p>
<pre><code>var RestaurantListVM = can.Map.extend({
define: {
restaurants: {
get: function() {
var state = this.attr('state'),
city = this.attr('city');
if(state && city) {
return Restaurant.findAll({
'address.state': state,
'address.city': city
});
}
return null;
}
}
}
});
</code></pre>
<p><a name="views"></a></p>
<h3>Views</h3>
<p>Views are passed a view-model and generate visual output that’s meaningful to a user - in our case that
output is HTML. Views are able to:</p>
<ul>
<li>Listen to changes in view-models and models and update the HTML (<strong>one-way bindings</strong>).</li>
<li>Listen to HTML events, like clicks, and call methods on the view-models and models (<strong>event bindings</strong>).</li>
<li>Listen to form elements changing and update view-model and model data (<strong>two-way bindings</strong>).</li>
</ul>
<p>In CanJS, the preferred method for creating views is using <a href="../docs/can.stache.html">can.stache</a>
templates. <code>can.stache</code> uses mustache/handlebars syntax. <code>can.stache</code>'s event and two-way binding
syntaxes can be found at <a href="../docs/can.view.bindings.html">can.view.bindings</a>.</p>
<p>At this time, <code>can.stache</code> is supplied as a supporting
library, which means you must explicitly add it to your application. We’ll see
how to do that when we set up our application in the next chapter. In 3.0,
Stache will part of the core CanJS lib.</p>
<p><a href="http://justinbmeyer.jsbin.com/gewavi/edit?html,output">Example: Generate HTML for the previous example's view-model.</a></p>
<pre><code><label>State</label>
{{#if states.isPending}}
<select disabled><option>Loading...</option></select>
{{else}}
<select {($value)}="state">
{{^if state}}
<option value="">Choose a state</option>
{{/if}}
{{#each states.value}}
<option value="{{short}}">{{name}}</option>
{{/each}}
</select>
{{/if}}
</code></pre>
<p><a name="custom_elements"></a></p>
<h3>Custom Elements</h3>
<p>Custom HTML Elements are how CanJS encapsulates and orchestrates different pieces of
functionality within an application. Custom elements are built with
<a href="../docs/can.Component.html">can.Component</a> and combine a
view-model and view.</p>
<p><a href="http://justinbmeyer.jsbin.com/sonuwuc/edit?html,js,output">Example: Encapsulate rich select behavior with a custom <select-loader> element.</a></p>
<pre><code><select-loader {promise}="states" {(value)}="state"
choose-text="Choose a state">
{{#each states.value}}
<option value="{{short}}">{{name}}</option>
{{/each}}
</select-loader>
</code></pre>
<p><a name="routing"></a></p>
<h3>Routing with an AppViewModel</h3>
<p>CanJS maintains a reciprocal relationship between the browser's url
and a <a href="../docs/can.Map.html">can.Map</a> view-model. This view-model instance
represents the state of the application as a whole and so is
called the <code>appViewModel</code>. When the url changes,
CanJS will update the properties of the <code>appViewModel</code>. When
the <code>appViewModel</code> changes, CanJS will update the url.</p>
<p><a href="../docs/can.route.html">can.route</a> is used to setup the relationship between the
<code>appViewModel</code> and the URL. It can be used with both <a href="../docs/can.route.pushstate.html">pushstate</a> and
hashchange (the default) routing.</p>
<p><a href="http://jsbin.com/surokag/edit?html,js,output">Example: Route between <home-page> and <restaurants-page> custom elements.</a></p>
<pre><code>{{#eq page 'home'}}
<home-page/>
{{else}}
<restaurants-page/>
{{/eq}}
</code></pre>
<pre><code>var AppViewModel = can.Map.extend({
define: {}
});
// Create an instance of that map
var appViewModel = new AppViewModel();
// Connect the map to the browser's URL
can.route.map(appViewModel);
// Define pretty routing rules
can.route(":page",{page: "home"});
// Start the two-way binding between the URL and the `appViewModel`.
can.route.ready();
</code></pre>
<p>Application ViewModels free developers
from worrying about what the url looks like. Instead, you focus on
updating the state of the application.</p>
<h2>Using the Getting Started Guide</h2>
<p>Each chapter in the Getting Started Guide is prefaced with an overview of the
topics covered in that chapter. The overview section also contains a link where
you can download a zip file containing the code relevant to that chapter, as follows:</p>
<hr />
<p><strong>In this Chapter</strong></p>
<ul>
<li>Topic 1</li>
<li>Topic 2</li>
<li>Connecting <code>can.Model</code>’s with <code>can.Component</code>’s</li>
</ul>
<p>Get the code for: <a href="/guides/examples/PlaceMyOrder/ch-0_canjs-getting-started.zip">chapter 0</a></p>
<hr />
<hr />
<p><span class="pull-right"><a href="Setup.html">Setup ›</a></span></p>
</div>
<script src="//static.jsbin.com/js/embed.min.js?3.35.5"></script>
</section>
<script type="text/javascript">
var docObject = {"src":{"path":"docs/can-guides/experiment/pmo/tutorial.md"},"description":"\n","name":"guides/pmo","title":"Place My Order Guide","type":"page","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>