can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
1,384 lines (1,074 loc) • 53.6 kB
HTML
<!DOCTYPE html>
<!--####################################################################
THIS IS A GENERATED FILE -- ANY CHANGES MADE WILL BE OVERWRITTEN
INSTEAD CHANGE:
source: [object Object]
@page guides/chat
######################################################################## -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>CanJS - Chat 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="
parent
expanded">
<a class="page"
href="../guides.html"
title="Welcome to CanJS! These guides are here to help you develop and improve your relationship with CanJS. After all, picking a JavaScript framework is a commitment. We want CanJS to be the
framework you marry. This page helps you know how advance through the different stages of this
relationship:">
Guides
</a>
<ul>
<li>
<span>introduction</span>
<ul>
<li class="
">
<a class="page"
href="mission.html"
title="Learn about CanJS's mission, why it matters, and how we've worked (and will keep working) to accomplish it.">
Mission
</a>
</li>
<li class="
">
<a class="page"
href="technical.html"
title="">
Technical Highlights
</a>
</li>
<li class="
">
<a class="page"
href="who-uses-canjs.html"
title="">
Who uses CanJS?
</a>
</li>
</ul>
</li>
<li>
<span>experiment</span>
<ul>
<li class="current
parent
expanded">
<a class="page"
href="chat.html"
title="This guide walks through building real time chat application with CanJS's Core libraries. It takes about 30 minutes to complete.">
Chat Guide
</a>
</li>
<li class="
">
<a class="page"
href="todomvc.html"
title="This guide walks through building a slightly modified version of TodoMVC with CanJS's Core libraries and can-fixture. It takes about 1 hour to complete.">
TodoMVC Guide
</a>
</li>
<li class="
">
<a class="page"
href="atm.html"
title="This guide walks through building and testing an ATM application with CanJS's
Core libraries. It teaches how to do test driven development (TDD)
and manage complex state. It takes about 2 hours to complete.">
ATM Guide
</a>
</li>
<li class="
">
<a class="page"
href="setup.html"
title="CanJS is packaged in multiple ways so that it can fit into any development workflow. Learn how to setup CanJS in different environments.">
Setting up CanJS
</a>
</li>
</ul>
</li>
<li>
<span>commitment</span>
<ul>
<li class="
">
<a class="page"
href="api.html"
title="This page walks through how to use and understand CanJS's API documentation.">
API Guide
</a>
</li>
<li class="
">
<a class="page"
href="examples.html"
title="">
Examples
</a>
</li>
<li class="
">
<a class="page"
href="../roadmap.html"
title="Learn about CanJS's future plans and how we make them, and how you can influence them.">
Roadmap
</a>
</li>
<li class="
">
<a class="page"
href="../migrate-3.html"
title="">
Migrating to 3.0
</a>
</li>
</ul>
</li>
<li>
<span>contribute</span>
<ul>
<li class="
">
<a class="page"
href="contributing/bug-report.html"
title="Learn how to submit a bug report.">
Bug Report
</a>
</li>
<li class="
">
<a class="page"
href="contributing/code.html"
title="Learn how contribute a code change to CanJS.">
Code
</a>
</li>
<li class="
">
<a class="page"
href="contributing/documentation.html"
title="Learn how to improve CanJS's site and documentation.">
Documentation
</a>
</li>
<li class="
">
<a class="page"
href="contributing/evangelism.html"
title="Learn about resources that can help you spread the word about CanJS.">
Evangelism
</a>
</li>
<li class="
">
<a class="page"
href="contributing/feature-suggestion.html"
title="Learn how to suggest a feature.">
Feature Suggestion
</a>
</li>
<li class="
">
<a class="page"
href="contributing/releases.html"
title="Release and hosting information for CanJS maintainers.">
Releases
</a>
</li>
</ul>
</li>
</ul>
</li>
<li class="
">
<a class="page"
href="../can-core.html"
title="The best, most hardened and generally useful libraries in CanJS.">
Core
</a>
</li>
<li class="
">
<a class="page"
href="../can-ecosystem.html"
title="Useful libraries that extend or add important features to the core collection.">
Ecosystem
</a>
</li>
<li class="
">
<a class="page"
href="../can-infrastructure.html"
title="Utility libraries that power the core and ecosystem collection.">
Infrastructure
</a>
</li>
<li class="
">
<a class="page"
href="../can-legacy.html"
title="Former libraries that we still accept patches for, but are not under active development.">
Legacy
</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="../../index.html">CanJS</a></li> /
<li><a href="../guides.html">Guides</a></li> /
<li><a href="chat.html">Chat 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>Chat Guide</h1>
<div>page</div>
</div>
<section class="description">
<p>This guide walks through building real time chat application with CanJS's <a href="../can-core.html" title="The best, most hardened and generally useful libraries in CanJS.">Core libraries</a>. It takes about 30 minutes to complete.</p>
</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/chat/chat.md">source</a></li>
<!-- <li><a href="#">download</a></li> -->
<!-- <li><a href="#">tests</a></li> -->
</ul>
</section>
<section class="body">
<h2>Setup</h2>
<p>The easiest way to get started is to clone the following JSBin by clicking the <strong>JS Bin</strong> button on the top left:</p>
<p><a class="jsbin-embed" href="http://jsbin.com/weganur/1/edit?html,output">JS Bin on jsbin.com</a></p>
<p>The JSBin loads <a href="http://getbootstrap.com/" title="Bootstrap">Bootstrap</a> for its styles. And <a href="http://socket.io/" title="socket.io">socket.io</a> for a socket
library. It will be connecting to a restful and real-time service layer at <a href="http://chat.donejs.com/api/messages" title="http://chat.donejs.com/api/messages">http://chat.donejs.com/api/messages</a>.</p>
<p>The JSBin also loads <a href="https://unpkg.com/can/dist/global/can.js">can.js</a>, which is a script that includes all of CanJS core under a
single global <code>can</code> namespace.</p>
<p>Generally speaking, you should not use the global can script and instead
should import things directly with a module loader like <a href="http://stealjs.com">StealJS</a>,
WebPack or Browserify. In a real app your code will look like:</p>
<pre><code class="language-js">var DefineMap = require("can-define/map/map");
var DefineList = require("can-define/list/list");
var Todo = DefineMap.extend({ ... });
Todo.List = DefineList.extend({ ... });
</code></pre>
<p>Not:</p>
<pre><code class="language-js">var Todo = can.DefineMap.extend({ ... });
Todo.List = can.DefineList.extend({ ... });
</code></pre>
<p>Read <a href="setup.html" title="CanJS is packaged in multiple ways so that it can fit into any development workflow. Learn how to setup CanJS in different environments.">Setting up CanJS</a> on how to setup CanJS in a real app.
Checkout <a href="https://donejs.com/Guide.html" title="the DoneJS version of this guide">the DoneJS version of this guide</a>.</p>
<h2>Hello World</h2>
<p>In this section, we will:</p>
<ul>
<li>Show a big "Chat Home" within a bootstrap container.</li>
<li>Make it when "Chat Home" is clicked, a "!" is added to the end of the title.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>To create a <code><script></code> tag containing the content of the <code>chat-template</code> template.</li>
<li>Have the content insert a <code>message</code> value within a responsive Bootstrap container using <a href="../can-stache.tags.escaped.html" title="Insert the value of the expression into the output of the template.">{{expression}}</a>.</li>
<li>Listen for <code>click</code> events and call <code>addExcitement</code> with <a href="../can-stache-bindings.event.html" title="Response to events on elements or component ViewModels.">(event)</a>.</li>
</ul>
<pre><code class="language-html"><!DOCTYPE html>
<html>
<head>
<meta name="description" content="Chat Guide 3.0 - Hello World">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<script type='text/stache' id='chat-template'>
<div class="container">
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
<h1 class="page-header text-center" ($click)="addExcitement()">
{{message}}
</h1>
</div>
</div>
</div>
</script>
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
<script src="https://unpkg.com/can/dist/global/can.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js"></script>
</body>
</html>
</code></pre>
<p><span line-highlight='12-22,only'></span>
Update the <code>JavaScript</code> tab to:</p>
<ul>
<li>Define an application view-model (<code>AppVM</code>) type by extending <a href="../can-define/map/map.html" title="Create observable objects.">can-define/map/map</a>. Its definition includes:
<ul>
<li>A <code>message</code> property that is a <a href="../can-define.types.html" title="Defines the type, initial value, and get, set, and serialize behavior for an
observable property. All type converters leave null and undefined as is except for
the "htmlbool" type converter.">string</a>
value <a href="../can-define.types.value.html" title="Returns the default value for instances of the defined type. The default value is defined on demand, when the property
is read for the first time.">initialized</a> to <code>"Chat Home"</code>.</li>
<li>An <code>addExcitement</code> method that adds <code>"!"</code> to the end of the <code>message</code> property.</li>
</ul></li>
<li>Create an instance of the <code>AppVM</code> type (<code>appVM</code>).</li>
<li>Compile a <a href="../can-stache.html" title="Live binding Mustache and Handlebars-compatible templates.">can-stache</a> <a href="../can-stache.renderer.html" title="A function returned by can-stache that renders a template into an html documentFragment.">template renderer</a> function from the contents of the <code><script></code> tag.</li>
<li>Render that template with <code>appVM</code> as a source of data into a <a href="https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment" title="document fragment">document fragment</a>.</li>
<li>Insert the document fragment into the <code><body></code> tag.</li>
</ul>
<pre><code class="language-js">var AppVM = can.DefineMap.extend({
message: {
type: "string",
value: "Chat Home"
},
addExcitement: function(){
this.message = this.message + "!";
}
});
var appVM = new AppVM();
var template = can.stache.from('chat-template');
var frag = template(appVM);
document.body.appendChild(frag);
</code></pre>
<p><span line-highlight='1-15,only'></span>
When complete, you should see a large "Chat Home" in the <code>Output</code> panel. Click on it and
things will get really exciting!</p>
<video controls>
<source src="../../docs/can-guides/experiment/chat/1-hello-world/completed.webm" type="video/webm">
<source src="../../docs/can-guides/experiment/chat/1-hello-world/completed.ogg" type="video/ogg">
</video>
<p>This step sets up the essential basics of a CanJS application - a
<a href="../can-stache.html" title="Live binding Mustache and Handlebars-compatible templates.">can-stache</a> template rendered with an observable application view model instance.</p>
<p>The properties and methods the template uses are defined in the <code>AppVM</code>
type. The <code>AppVM</code> type extends <a href="../can-define/map/map.html" title="Create observable objects.">can-define/map/map</a>. We
defined a <code>message</code> and an <code>addExcitement</code> method.</p>
<p>We then created an instance of the <code>appVM</code> with the <code>new</code> operator. This created
an object with a <code>message</code> property and <code>addExcitement</code> method. For example, adding:</p>
<pre><code>console.log(appVM.message)
appVM.addExcitement();
console.log(appVM.message)
</code></pre>
<p>Will print out <code>"Chat Home"</code> and then <code>"Chat Home!"</code>.</p>
<p><code>DefineMap</code> instances are observable. This is why when <code>message</code> changes,
the template updates automatically.</p>
<p>The templates are a dialect of <a href="https://github.com/janl/mustache.js">mustache</a> and <a href="https://github.com/wycats/handlebars.js/">handlebars</a> syntax. The
mustache syntax allows a very terse writing style for the most common
patterns within templates:</p>
<ul>
<li>inserting data with <a href="../can-stache.tags.escaped.html" title="Insert the value of the expression into the output of the template.">{{expression}}</a></li>
<li>looping with <a href="../can-stache.helpers.each.html" title="">{{#each expression}}</a></li>
<li>branching with <a href="../can-stache.helpers.if.html" title="can-stache.helpers.if">{{#if expression}}</a> or <a href="../can-stache.helpers.is.html" title="Render something if two values are equal.">{{#is expressions}}</a></li>
</ul>
<blockquote>
<p><strong>Key take away:</strong> You define types like <code>AppVM</code> with method and property behaviors.
Instances of those types are observable by <a href="../can-stache.html" title="Live binding Mustache and Handlebars-compatible templates.">can-stache</a> templates.</p>
</blockquote>
<h2>Route between two pages</h2>
<p>In this section we will:</p>
<ul>
<li>Create a <strong>home page</strong> and <strong>chat messages page</strong> that the user can navigate between
with links and the browser's back and forward button.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Check if the <code>appVM</code>'s <code>page</code> property is 'home'. If it is, render the <strong>home
page</strong>'s content. If it's not, it will render the <strong>chat messages page</strong>'s content (using <a href="../can-stache.helpers.else.html" title="">{{else}}</a>).</li>
<li>Use <a href="../can-stache.helpers.routeUrl.html" title="Returns a url using route.url.">{{routeUrl hashes}}</a> to create the right link urls so that <code>page</code>
will be set on <code>appVM</code> to either <code>"home"</code> or <code>"chat"</code>.</li>
</ul>
<pre><code class="language-html"><!DOCTYPE html>
<html>
<head>
<meta name="description" content="Chat Guide 3.0 - Routing">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<script type='text/stache' id='chat-template'>
<div class="container">
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
{{#eq page 'home'}}
<h1 class="page-header text-center" ($click)="addExcitement()">
{{message}}
</h1>
<a href="{{routeUrl page='chat' }}"
class="btn btn-primary btn-block btn-lg">
Start chat
</a>
{{else}}
<h1 class="page-header text-center">
Chat Messages
</h1>
<h5><a href="{{routeUrl page='home'}}">Home</a></h5>
{{/eq}}
</div>
</div>
</div>
</script>
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
<script src="https://unpkg.com/can/dist/global/can.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js"></script>
</body>
</html>
</code></pre>
<p><span line-highlight='16-29,only'></span>
Update the <code>JavaScript</code> tab to:</p>
<ul>
<li>Add a <code>page</code> property that will be updated when the browser's URL changes.</li>
<li>Prevent the <code>message</code> property from becoming part of the URL changes.</li>
<li>Connect changes in the url to changes in the <code>appVM</code> with <a href="../can-route.data.html" title="This is the internal map underlying can-route. It can be set in order to cross-bind a top level state object (Application ViewModel) to can-route.">data</a>.</li>
<li>Create a pretty routing rule so if the url looks like <code>"#!chat"</code>, the <code>page</code> property of
<code>appVM</code> will be set to <code>chat</code> with <a href="../can-route.html" title="Manage browser history and client state by synchronizing the window.location.hash with a map.">can-route</a>. If there is nothing in the hash, <code>page</code>
will be set to <code>"home"</code>.</li>
<li>Initialize the url's values on <code>appVM</code> and setup the two way connection with <a href="../can-route.ready.html" title="Initializes can-route.">ready</a>.</li>
</ul>
<pre><code class="language-js">var AppVM = can.DefineMap.extend({
page: "string",
message: {
type: "string",
value: "Chat Home",
serialize: false
},
addExcitement: function(){
this.message = this.message + "!";
}
});
var appVM = new AppVM();
can.route.data = appVM;
can.route("{page}",{page: "home"});
can.route.ready();
var template = can.stache.from('chat-template');
var frag = template(appVM);
document.body.appendChild(frag);
</code></pre>
<p><span line-highlight='2,6,15-17,only'></span>
When complete, you should be able to toggle between the two pages. If you type:</p>
<pre><code>window.location.hash
</code></pre>
<p>in JSBin's console tab after clicking a new page, you will be able to see the hash change between <code>!#</code> and <code>#!chat</code>.</p>
<p>This step sets up a basic routing between different "pages" in an application.
CanJS's routing is based on the properties in the application view model. When
those properties change, different content is shown.</p>
<p>We connected the application view model to the routing system with <a href="../can-route.data.html" title="This is the internal map underlying can-route. It can be set in order to cross-bind a top level state object (Application ViewModel) to can-route.">can-route.data</a>
and initialized that connection with <a href="../can-route.ready.html" title="Initializes can-route.">can-route.ready</a>.</p>
<p>This makes it so if the <code>page</code> property changes, the browser's url will change. If the
browser's url changes, the <code>page</code> property changes.</p>
<blockquote>
<p><strong>Key take away:</strong> <a href="../can-route.html" title="Manage browser history and client state by synchronizing the window.location.hash with a map.">can-route</a> connects changes in the browser's url to
changes in the application view model and vice versa. Use changes in
the application view model to control which content is shown.</p>
</blockquote>
<h2>Chat Messages Component</h2>
<p>In this section, we will:</p>
<ul>
<li>Define and use a custom <code><chat-message></code> element that contains the behavior of the <strong>chat messages page</strong>.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Use the <code><chat-messages/></code> element.</li>
<li>Create a template for the <code><chat-messages/></code> element that contains the content of the
<strong>chat messages page</strong>.</li>
</ul>
<pre><code class="language-html"><!DOCTYPE html>
<html>
<head>
<meta name="description" content="Chat Guide 3.0 - Chat Messages">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<script type='text/stache' id='chat-template'>
<div class="container">
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
{{#eq page 'home'}}
<h1 class="page-header text-center" ($click)="addExcitement()">
{{message}}
</h1>
<a href="{{routeUrl page='chat' }}"
class="btn btn-primary btn-block btn-lg">
Start chat
</a>
{{else}}
<chat-messages/>
{{/eq}}
</div>
</div>
</div>
</script>
<script type='text/stache' id='chat-messages-template'>
<h1 class="page-header text-center">
Chat Messages
</h1>
<h5><a href="{{routeUrl page='home'}}">Home</a></h5>
</script>
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
<script src="https://unpkg.com/can/dist/global/can.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js"></script>
</body>
</html>
</code></pre>
<p><span line-highlight='25,32-37,only'></span>
Update the <code>JavaScript</code> tab to:</p>
<ul>
<li>Define a view model for the custom element (<code>ChatMessagesVM</code>).</li>
<li>Using <a href="../can-component.html" title="Create a custom element that can be used to manage widgets or application logic.">can-component</a> define a custom element that will render its <code>view</code> template with
an instance of its <code>ViewModel</code>.</li>
</ul>
<pre><code class="language-js">var ChatMessagesVM = can.DefineMap.extend({
});
can.Component.extend({
tag: "chat-messages",
ViewModel: ChatMessagesVM,
view: can.stache.from("chat-messages-template")
});
var AppVM = can.DefineMap.extend({
route: "string",
message: {
type: "string",
value: "Chat Home",
serialize: false
},
addExcitement: function(){
this.message = this.message + "!";
}
});
var appVM = new AppVM();
can.route.data = appVM;
can.route("{page}",{page: "home"});
can.route.ready();
var template = can.stache.from('chat-template');
var frag = template(appVM);
document.body.appendChild(frag);
</code></pre>
<p><span line-highlight='1-9,only'></span>
When complete, you should see the same behavior as the previous step. You should
be able to click back and forth between the two different pages.</p>
<p>This step creates the <code><chat-messages></code> custom element. Custom elements are used
to represent some grouping of related (and typically visual) functionality such as:</p>
<ul>
<li>Widgets like <code><my-slider></code>, or <code><acme-navigation></code>.</li>
<li>Pages like <code><chat-login></code> or <code><chat-messages></code>.</li>
</ul>
<p>Custom elements are the macroscopic building blocks of an application. They
are the <a href="https://en.wikipedia.org/wiki/Orchestration_(computing)">orchestration pieces</a>
used to assemble the application into a whole.</p>
<p>For example, an application's template might assemble many custom elements
to work together like:</p>
<pre><code>{{#if session}}
<app-toolbar {(selected-files)}="selectedFiles"/>
<app-directory {(selected-files)}="selectedFiles"/>
<app-files {(selected-files)}="selectedFiles"/>
<app-file-details {(selected-files)}="selectedFiles"/>
{{else}}
<app-login/>
{{/if}}
</code></pre>
<p>Breaking down an application into many isolated, and potentially reusable components
is a critical piece of CanJS software architecture.</p>
<p>Custom elements are defined with <a href="../can-component.html" title="Create a custom element that can be used to manage widgets or application logic.">can-component</a>. Components render their <code>view</code> within
the element with an instance of their <code>ViewModel</code>. By default their <code>view</code> only
has access to that data in the <code>ViewModel</code>. You can use <a href="../can-stache-bindings.html" title="Provides template event, one-way, and two-way bindings.">event and data bindings</a> like <a href="../can-stache-bindings.toChild.html" title="One-way bind a value in the parent scope to the viewModel.">{to-child}</a> and <a href="../can-stache-bindings.twoWay.html" title="Two-way bind a value in the viewModel or the element to the parent scope.">{(two-way)}</a> to pass data
between custom elements.</p>
<blockquote>
<p><strong>Key take away:</strong> <a href="../can-component.html" title="Create a custom element that can be used to manage widgets or application logic.">can-component</a> makes custom elements. Break down your application
into many bit-sized custom elements.</p>
</blockquote>
<h2>List Messages</h2>
<p>In this section, we will:</p>
<ul>
<li>Display messages from <a href="http://chat.donejs.com/api/messages">http://chat.donejs.com/api/messages</a></li>
<li>Show a "Loading..." message while the messages are loading.</li>
<li>Show an error if those messages fail to load.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Check if the messages are in the process of loading and show a loading indicator.</li>
<li>Check if the messages failed to load and display the reason for the failure.</li>
<li>If messages successfully loaded, list each message's name and body. If there
are no messages, write out "No messages".</li>
</ul>
<pre><code class="language-html"><!DOCTYPE html>
<html>
<head>
<meta name="description" content="Chat Guide 3.0 - List Messages">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<script type='text/stache' id='chat-template'>
<div class="container">
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
{{#eq page 'home'}}
<h1 class="page-header text-center" ($click)="addExcitement()">
{{message}}
</h1>
<a href="{{routeUrl page='chat' }}"
class="btn btn-primary btn-block btn-lg">
Start chat
</a>
{{else}}
<chat-messages/>
{{/eq}}
</div>
</div>
</div>
</script>
<script type='text/stache' id='chat-messages-template'>
<h1 class="page-header text-center">
Chat Messages
</h1>
<h5><a href="{{routeUrl page='home'}}">Home</a></h5>
{{#if messagesPromise.isPending}}
<div class="list-group-item list-group-item-info">
<h4 class="list-group-item-heading">Loading...</h4>
</div>
{{/if}}
{{#if messagesPromise.isRejected}}
<div class="list-group-item list-group-item-danger">
<h4 class="list-group3--item-heading">Error</h4>
<p class="list-group-item-text">{{messagesPromise.reason}}</p>
</div>
{{/if}}
{{#if messagesPromise.isResolved}}
{{#each messagesPromise.value}}
<div class="list-group-item">
<h4 class="list-group3--item-heading">{{name}}</h4>
<p class="list-group-item-text">{{body}}</p>
</div>
{{else}}
<div class="list-group-item">
<h4 class="list-group-item-heading">No messages</h4>
</div>
{{/each}}
{{/if}}
</script>
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
<script src="https://unpkg.com/can/dist/global/can.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js"></script>
</body>
</html>
</code></pre>
<p><span line-highlight='38-60,only'></span>
Update the <code>JavaScript</code> tab to:</p>
<ul>
<li>Define a <code>Message</code> type with <a href="../can-define/map/map.html" title="Create observable objects.">can-define/map/map</a>.</li>
<li>Define a <code>Message.List</code> type that contains <code>Message</code> items.</li>
<li>Connect the <code>Message</code> and <code>Message.List</code> type to
the restful messages service at <code>http://chat.donejs.com/api/messages</code>
using <a href="../can-connect/can/super-map/super-map.html" title="Create connection with many of the best behaviors in can-connect and hook it up to
a map.">can-connect/can/super-map/super-map</a>.</li>
<li>Create a <code>messagesPromise</code> property on <code>ChatMessagesVM</code> that's
<a href="../can-define.types.value.html" title="Returns the default value for instances of the defined type. The default value is defined on demand, when the property
is read for the first time.">value</a> is initialized to a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a>
that represents the loading of all messages using <a href="../can-connect/can/map/map.getList.html" title="Gets a list of instances of the map type.">getList</a>.</li>
</ul>
<pre><code class="language-js">var Message = can.DefineMap.extend({
id: "number",
name: "string",
body: "string",
created_at: "date"
});
Message.List = can.DefineList.extend({
"#": Message
});
Message.connection = can.connect.superMap({
url: {
resource: 'http://chat.donejs.com/api/messages',
contentType: 'application/x-www-form-urlencoded'
},
Map: Message,
List: Message.List,
name: 'message'
});
var ChatMessagesVM = can.DefineMap.extend({
messagesPromise: {
value: function(){
return Message.getList({});
}
}
});
can.Component.extend({
tag: "chat-messages",
ViewModel: ChatMessagesVM,
view: can.stache.from("chat-messages-template")
});
var AppVM = can.DefineMap.extend({
page: "string",
message: {
type: "string",
value: "Chat Home",
serialize: false
},
addExcitement: function(){
this.message = this.message + "!";
}
});
var appVM = new AppVM();
can.route.data = appVM;
can.route("{page}",{page: "home"});
can.route.ready();
var template = can.stache.from('chat-template');
var frag = template(appVM);
document.body.appendChild(frag);
</code></pre>
<p><span line-highlight='1-20,23-27,only'></span>
When complete, you should see a list of messages in the <strong>chat messages page</strong>.</p>
<p>This step creates a <code>Message</code> model, by first creating the <code>Message</code> type
and then connecting it to a messages service at <code>http://chat.donejs.com/api/messages</code>. This
adds <a href="../can-connect/can/map/map.html" title="Make a connection use a map type.">methods</a> to the <code>Message</code> type that let you:</p>
<ul>
<li><p>Get a list of messages:</p>
<pre><code class="language-js">Message.getList({}).then(function(messages){})
</code></pre></li>
<li><p>Get a single message:</p>
<pre><code class="language-js">Message.get({id: 5}).then(function(message){})
</code></pre></li>
<li><p>Create a message on the server:</p>
<pre><code class="language-js">message = new Message({name: "You", body: "Hello World"})
message.save()
</code></pre></li>
<li><p>Update a message on the server:</p>
<pre><code class="language-js">message.body = "Welcome Earth!";
message.save();
</code></pre></li>
<li><p>Delete message on the server:</p>
<pre><code class="language-js">message.destroy();
</code></pre></li>
</ul>
<p>There are also methods to let you know when a message
<a href="../can-connect/can/map/map.prototype.isNew.html" title="Returns if the map has not been persisted.">isNew</a>,
<a href="../can-connect/can/map/map.prototype.isSaving.html" title="Returns if the map is currently being saved.">isSaving</a>, and
<a href="../can-connect/can/map/map.prototype.isDestroying.html" title="Returns if the map is currently being destroyed.">isDestroying</a>.</p>
<p>With the message model created, it's used to load and list messages on the server.</p>
<blockquote>
<p><strong>Key take away:</strong> Create a model to connect to backend data.</p>
</blockquote>
<h2>Create Messages</h2>
<p>In this section, we will:</p>
<ul>
<li>Add the ability to create messages on the server and have them added to the list of messages.</li>
</ul>
<p>Update the <code>HTML</code> tab to:</p>
<ul>
<li>Create a form to enter a message's <code>name</code> and <code>body</code>.</li>
<li>When the form is submitted, call <code>send</code> on the <code>ChatMessagesVM</code> with <a href="../can-stache-bindings.event.html" title="Response to events on elements or component ViewModels.">(event)</a>.</li>
<li>Connect the first <code><input></code>'s <code>value</code> to the <code>ChatMessagesVM</code>'s <code>name</code> property with <a href="../can-stache-bindings.twoWay.html" title="Two-way bind a value in the viewModel or the element to the parent scope.">{(two-way)}</a>.</li>
<li>Connect the second <code><input></code>'s <code>value</code> to the <code>ChatMessagesVM</code>'s <code>body</code> property with <a href="../can-stache-bindings.twoWay.html" title="Two-way bind a value in the viewModel or the element to the parent scope.">{(two-way)}</a>.</li>
</ul>
<pre><code class="language-html"><!DOCTYPE html>
<html>
<head>
<meta name="description" content="Chat Guide 3.0 - Create Messages">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
</head>
<body>
<script type='text/stache' id='chat-template'>
<div class="container">
<div class="row">
<div class="col-sm-8 col-sm-offset-2">
{{#eq page 'home'}}
<h1 class="page-header text-center" ($click)="addExcitement()">
{{message}}
</h1>
<a href="{{routeUrl page='chat' }}"
class="btn btn-primary btn-block btn-lg">
Start chat
</a>
{{else}}
<chat-messages/>
{{/eq}}
</div>
</div>
</div>
</script>
<script type='text/stache' id='chat-messages-template'>
<h1 class="page-header text-center">
Chat Messages
</h1>
<h5><a href="{{routeUrl page='home'}}">Home</a></h5>
{{#if messagesPromise.isPending}}
<div class="list-group-item list-group-item-info">
<h4 class="list-group-item-heading">Loading...</h4>
</div>
{{/if}}
{{#if messagesPromise.isRejected}}
<div class="list-group-item list-group-item-danger">
<h4 class="list-group3--item-heading">Error</h4>
<p class="list-group-item-text">{{messagesPromise.reason}}</p>
</div>
{{/if}}
{{#if messagesPromise.isResolved}}
{{#each messagesPromise.value}}
<div class="list-group-item">
<h4 class="list-group3--item-heading">{{name}}</h4>
<p class="list-group-item-text">{{body}}</p>
</div>
{{else}}
<div class="list-group-item">
<h4 class="list-group-item-heading">No messages</h4>
</div>
{{/each}}
{{/if}}
<form class="row" ($submit)="send(%event)">
<div class="col-sm-3">
<input type="text" class="form-control" placeholder="Your name"
{($value)}="name"/>
</div>
<div class="col-sm-6">
<input type="text" class="form-control" placeholder="Your message"
{($value)}="body"/>
</div>
<div class="col-sm-3">
<input type="submit" class="btn btn-primary btn-block" value="Send"/>
</div>
</form>
</script>
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
<script src="https://unpkg.com/can/dist/global/can.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js"></script>
</body>
</html>
</code></pre>
<p><span line-highlight='62-74,only'></span>
Update the <code>JS</code> tab to:</p>
<ul>
<li>Define a <code>name</code> and <code>body</code> property on <code>ChatMessagesVM</code>.</li>
<li>Define a <code>send</code> method on <code>ChatMessagesVM</code> that creates a new <code>Message</code> and saves it to the server.</li>
</ul>
<pre><code class="language-js">var Message = can.DefineMap.extend({
id: "number",
name: "string",
body: "string",
created_at: "date"
});
Message.List = can.DefineList.extend({
"#": Message
});
Message.connection = can.connect.superMap({
url: {
resource: 'http://chat.donejs.com/api/messages',
contentType: 'application/x-www-form-urlencoded'
},
Map: Message,
List: Message.List,
name: 'message'
});
var ChatMessagesVM = can.DefineMap.extend({
messagesPromise: {
value: function(){
return Message.getList({});
}
},
name: "string",
body: "string",
send: function(event) {
event.preventDefault();
new Message({
name: this.name,
body: this.body
}).save().then(function(){
this.body = "";
}.bind(this));
}
});
can.Component.extend({
tag: "chat-messages",
ViewModel: ChatMessagesVM,
view: can.stache.from("chat-messages-template")
});
var AppVM = can.DefineMap.extend({
page: "string",
message: {
type: "string",
value: "Chat Home",
serialize: false
},
addExcitement: function(){
this.message = this.message + "!";
}
});
var appVM = new AppVM();
can.route.data = appVM;
can.route("{page}",{page: "home"});
can.route.ready();
var template = can.stache.from('chat-template');
var frag = template(appVM);
document.body.appendChild(frag);
</code></pre>
<p><span line-highlight='28-39,only'></span>
When complete, you will be able to create messages and have them appear in the list.</p>
<p>This step sets up a form to create a <code>Message</code> on the server.
Notice that the new <code>Message</code> automatically appears in the list of messages. This
is because <a href="../can-connect/can/super-map/super-map.html" title="Create connection with many of the best behaviors in can-connect and hook it up to
a map.">can-connect/can/super-map/super-map</a> adds the <a href="../can-connect/real-time/real-time.html" title="Update lists to include or exclude instances based
on set logic.">can-connect/real-time/real-time</a> behavior. The
<a href="../can-connect/real-time/real-time.html" title="Update lists to include or exclude instances based
on set logic.">can-connect/real-time/real-time</a> behavior automatically inserts newly created messages into
lists that they belong within. This is one of CanJS's best features - automatic list management.</p>
<blockquote>
<p><strong>Key take away:</strong> CanJS will add, remove, and update lists for you automatically.</p>
</blockquote>
<h2>Real Time</h2>
<p>In this section, we will:</p>
<ul>
<li>Listen to messages created by other users and add them to the list of messages.</li>
</ul>
<p>Update the <code>JavaScript</code> tab to:</p>
<ul>
<li>Create a <a href="http://socket.io/" title="http://socket.io/">http://socket.io/</a> connection (<code>socket</code>).</li>
<li>Listen to when messages are created, updated, and destroyed and and call the
corresponding <a href="../can-connect/real-time/real-time.html" title="Update lists to include or exclude instances based
on set logic.">can-connect/real-time/real-time</a> methods.</li>
</ul>
<pre><code class="language-js">var Message = can.DefineMap.extend({
id: "number",
name: "string",
body: "string",
created_at: "date"
});
Message.List = can.DefineList.extend({
"#": Message
});
Message.connection = can.connect.superMap({
url: {
resource: 'http://chat.donejs.com/api/messages',
contentType: 'application/x-www-form-urlencoded'
},
Map: Message,
List: Message.List,
name: 'message'
});
var socket = io('http://chat.donejs.com');
socket.on('messages created', function(message){
Message.connection.createInstance(message);
});
socket.on('mess