UNPKG

can

Version:

MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.

1,384 lines (1,074 loc) 53.6 kB
<!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> &nbsp; </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&#x27;s mission, why it matters, and how we&#x27;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&#x27;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&#x27;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&#x27;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&#x27;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&#x27;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&#x27;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(&quot;can-define/map/map&quot;); var DefineList = require(&quot;can-define/list/list&quot;); 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 &quot;Chat Home&quot; within a bootstrap container.</li> <li>Make it when &quot;Chat Home&quot; is clicked, a &quot;!&quot; 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>&lt;script&gt;</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">&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta name=&quot;description&quot; content=&quot;Chat Guide 3.0 - Hello World&quot;&gt; &lt;meta charset=&quot;utf-8&quot;&gt; &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt; &lt;title&gt;JS Bin&lt;/title&gt; &lt;link rel=&quot;stylesheet&quot; href=&quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&quot;&gt; &lt;/head&gt; &lt;body&gt; &lt;script type='text/stache' id='chat-template'&gt; &lt;div class=&quot;container&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-sm-8 col-sm-offset-2&quot;&gt; &lt;h1 class=&quot;page-header text-center&quot; ($click)=&quot;addExcitement()&quot;&gt; {{message}} &lt;/h1&gt; &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/script&gt; &lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://unpkg.com/can/dist/global/can.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js&quot;&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </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 &quot;htmlbool&quot; 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>&quot;Chat Home&quot;</code>.</li> <li>An <code>addExcitement</code> method that adds <code>&quot;!&quot;</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>&lt;script&gt;</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>&lt;body&gt;</code> tag.</li> </ul> <pre><code class="language-js">var AppVM = can.DefineMap.extend({ message: { type: &quot;string&quot;, value: &quot;Chat Home&quot; }, addExcitement: function(){ this.message = this.message + &quot;!&quot;; } }); 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 &quot;Chat Home&quot; 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>&quot;Chat Home&quot;</code> and then <code>&quot;Chat Home!&quot;</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>&quot;home&quot;</code> or <code>&quot;chat&quot;</code>.</li> </ul> <pre><code class="language-html">&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta name=&quot;description&quot; content=&quot;Chat Guide 3.0 - Routing&quot;&gt; &lt;meta charset=&quot;utf-8&quot;&gt; &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt; &lt;title&gt;JS Bin&lt;/title&gt; &lt;link rel=&quot;stylesheet&quot; href=&quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&quot;&gt; &lt;/head&gt; &lt;body&gt; &lt;script type='text/stache' id='chat-template'&gt; &lt;div class=&quot;container&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-sm-8 col-sm-offset-2&quot;&gt; {{#eq page 'home'}} &lt;h1 class=&quot;page-header text-center&quot; ($click)=&quot;addExcitement()&quot;&gt; {{message}} &lt;/h1&gt; &lt;a href=&quot;{{routeUrl page='chat' }}&quot; class=&quot;btn btn-primary btn-block btn-lg&quot;&gt; Start chat &lt;/a&gt; {{else}} &lt;h1 class=&quot;page-header text-center&quot;&gt; Chat Messages &lt;/h1&gt; &lt;h5&gt;&lt;a href=&quot;{{routeUrl page='home'}}&quot;&gt;Home&lt;/a&gt;&lt;/h5&gt; {{/eq}} &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/script&gt; &lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://unpkg.com/can/dist/global/can.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js&quot;&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </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>&quot;#!chat&quot;</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>&quot;home&quot;</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: &quot;string&quot;, message: { type: &quot;string&quot;, value: &quot;Chat Home&quot;, serialize: false }, addExcitement: function(){ this.message = this.message + &quot;!&quot;; } }); var appVM = new AppVM(); can.route.data = appVM; can.route(&quot;{page}&quot;,{page: &quot;home&quot;}); 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 &quot;pages&quot; 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>&lt;chat-message&gt;</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>&lt;chat-messages/&gt;</code> element.</li> <li>Create a template for the <code>&lt;chat-messages/&gt;</code> element that contains the content of the <strong>chat messages page</strong>.</li> </ul> <pre><code class="language-html">&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta name=&quot;description&quot; content=&quot;Chat Guide 3.0 - Chat Messages&quot;&gt; &lt;meta charset=&quot;utf-8&quot;&gt; &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt; &lt;title&gt;JS Bin&lt;/title&gt; &lt;link rel=&quot;stylesheet&quot; href=&quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&quot;&gt; &lt;/head&gt; &lt;body&gt; &lt;script type='text/stache' id='chat-template'&gt; &lt;div class=&quot;container&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-sm-8 col-sm-offset-2&quot;&gt; {{#eq page 'home'}} &lt;h1 class=&quot;page-header text-center&quot; ($click)=&quot;addExcitement()&quot;&gt; {{message}} &lt;/h1&gt; &lt;a href=&quot;{{routeUrl page='chat' }}&quot; class=&quot;btn btn-primary btn-block btn-lg&quot;&gt; Start chat &lt;/a&gt; {{else}} &lt;chat-messages/&gt; {{/eq}} &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/script&gt; &lt;script type='text/stache' id='chat-messages-template'&gt; &lt;h1 class=&quot;page-header text-center&quot;&gt; Chat Messages &lt;/h1&gt; &lt;h5&gt;&lt;a href=&quot;{{routeUrl page='home'}}&quot;&gt;Home&lt;/a&gt;&lt;/h5&gt; &lt;/script&gt; &lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://unpkg.com/can/dist/global/can.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js&quot;&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </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: &quot;chat-messages&quot;, ViewModel: ChatMessagesVM, view: can.stache.from(&quot;chat-messages-template&quot;) }); var AppVM = can.DefineMap.extend({ route: &quot;string&quot;, message: { type: &quot;string&quot;, value: &quot;Chat Home&quot;, serialize: false }, addExcitement: function(){ this.message = this.message + &quot;!&quot;; } }); var appVM = new AppVM(); can.route.data = appVM; can.route(&quot;{page}&quot;,{page: &quot;home&quot;}); 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>&lt;chat-messages&gt;</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>&lt;my-slider&gt;</code>, or <code>&lt;acme-navigation&gt;</code>.</li> <li>Pages like <code>&lt;chat-login&gt;</code> or <code>&lt;chat-messages&gt;</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}} &lt;app-toolbar {(selected-files)}=&quot;selectedFiles&quot;/&gt; &lt;app-directory {(selected-files)}=&quot;selectedFiles&quot;/&gt; &lt;app-files {(selected-files)}=&quot;selectedFiles&quot;/&gt; &lt;app-file-details {(selected-files)}=&quot;selectedFiles&quot;/&gt; {{else}} &lt;app-login/&gt; {{/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 &quot;Loading...&quot; 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 &quot;No messages&quot;.</li> </ul> <pre><code class="language-html">&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta name=&quot;description&quot; content=&quot;Chat Guide 3.0 - List Messages&quot;&gt; &lt;meta charset=&quot;utf-8&quot;&gt; &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt; &lt;title&gt;JS Bin&lt;/title&gt; &lt;link rel=&quot;stylesheet&quot; href=&quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&quot;&gt; &lt;/head&gt; &lt;body&gt; &lt;script type='text/stache' id='chat-template'&gt; &lt;div class=&quot;container&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-sm-8 col-sm-offset-2&quot;&gt; {{#eq page 'home'}} &lt;h1 class=&quot;page-header text-center&quot; ($click)=&quot;addExcitement()&quot;&gt; {{message}} &lt;/h1&gt; &lt;a href=&quot;{{routeUrl page='chat' }}&quot; class=&quot;btn btn-primary btn-block btn-lg&quot;&gt; Start chat &lt;/a&gt; {{else}} &lt;chat-messages/&gt; {{/eq}} &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/script&gt; &lt;script type='text/stache' id='chat-messages-template'&gt; &lt;h1 class=&quot;page-header text-center&quot;&gt; Chat Messages &lt;/h1&gt; &lt;h5&gt;&lt;a href=&quot;{{routeUrl page='home'}}&quot;&gt;Home&lt;/a&gt;&lt;/h5&gt; {{#if messagesPromise.isPending}} &lt;div class=&quot;list-group-item list-group-item-info&quot;&gt; &lt;h4 class=&quot;list-group-item-heading&quot;&gt;Loading...&lt;/h4&gt; &lt;/div&gt; {{/if}} {{#if messagesPromise.isRejected}} &lt;div class=&quot;list-group-item list-group-item-danger&quot;&gt; &lt;h4 class=&quot;list-group3--item-heading&quot;&gt;Error&lt;/h4&gt; &lt;p class=&quot;list-group-item-text&quot;&gt;{{messagesPromise.reason}}&lt;/p&gt; &lt;/div&gt; {{/if}} {{#if messagesPromise.isResolved}} {{#each messagesPromise.value}} &lt;div class=&quot;list-group-item&quot;&gt; &lt;h4 class=&quot;list-group3--item-heading&quot;&gt;{{name}}&lt;/h4&gt; &lt;p class=&quot;list-group-item-text&quot;&gt;{{body}}&lt;/p&gt; &lt;/div&gt; {{else}} &lt;div class=&quot;list-group-item&quot;&gt; &lt;h4 class=&quot;list-group-item-heading&quot;&gt;No messages&lt;/h4&gt; &lt;/div&gt; {{/each}} {{/if}} &lt;/script&gt; &lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://unpkg.com/can/dist/global/can.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js&quot;&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </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: &quot;number&quot;, name: &quot;string&quot;, body: &quot;string&quot;, created_at: &quot;date&quot; }); Message.List = can.DefineList.extend({ &quot;#&quot;: 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: &quot;chat-messages&quot;, ViewModel: ChatMessagesVM, view: can.stache.from(&quot;chat-messages-template&quot;) }); var AppVM = can.DefineMap.extend({ page: &quot;string&quot;, message: { type: &quot;string&quot;, value: &quot;Chat Home&quot;, serialize: false }, addExcitement: function(){ this.message = this.message + &quot;!&quot;; } }); var appVM = new AppVM(); can.route.data = appVM; can.route(&quot;{page}&quot;,{page: &quot;home&quot;}); 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: &quot;You&quot;, body: &quot;Hello World&quot;}) message.save() </code></pre></li> <li><p>Update a message on the server:</p> <pre><code class="language-js">message.body = &quot;Welcome Earth!&quot;; 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>&lt;input&gt;</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>&lt;input&gt;</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">&lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;meta name=&quot;description&quot; content=&quot;Chat Guide 3.0 - Create Messages&quot;&gt; &lt;meta charset=&quot;utf-8&quot;&gt; &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&gt; &lt;title&gt;JS Bin&lt;/title&gt; &lt;link rel=&quot;stylesheet&quot; href=&quot;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css&quot;&gt; &lt;/head&gt; &lt;body&gt; &lt;script type='text/stache' id='chat-template'&gt; &lt;div class=&quot;container&quot;&gt; &lt;div class=&quot;row&quot;&gt; &lt;div class=&quot;col-sm-8 col-sm-offset-2&quot;&gt; {{#eq page 'home'}} &lt;h1 class=&quot;page-header text-center&quot; ($click)=&quot;addExcitement()&quot;&gt; {{message}} &lt;/h1&gt; &lt;a href=&quot;{{routeUrl page='chat' }}&quot; class=&quot;btn btn-primary btn-block btn-lg&quot;&gt; Start chat &lt;/a&gt; {{else}} &lt;chat-messages/&gt; {{/eq}} &lt;/div&gt; &lt;/div&gt; &lt;/div&gt; &lt;/script&gt; &lt;script type='text/stache' id='chat-messages-template'&gt; &lt;h1 class=&quot;page-header text-center&quot;&gt; Chat Messages &lt;/h1&gt; &lt;h5&gt;&lt;a href=&quot;{{routeUrl page='home'}}&quot;&gt;Home&lt;/a&gt;&lt;/h5&gt; {{#if messagesPromise.isPending}} &lt;div class=&quot;list-group-item list-group-item-info&quot;&gt; &lt;h4 class=&quot;list-group-item-heading&quot;&gt;Loading...&lt;/h4&gt; &lt;/div&gt; {{/if}} {{#if messagesPromise.isRejected}} &lt;div class=&quot;list-group-item list-group-item-danger&quot;&gt; &lt;h4 class=&quot;list-group3--item-heading&quot;&gt;Error&lt;/h4&gt; &lt;p class=&quot;list-group-item-text&quot;&gt;{{messagesPromise.reason}}&lt;/p&gt; &lt;/div&gt; {{/if}} {{#if messagesPromise.isResolved}} {{#each messagesPromise.value}} &lt;div class=&quot;list-group-item&quot;&gt; &lt;h4 class=&quot;list-group3--item-heading&quot;&gt;{{name}}&lt;/h4&gt; &lt;p class=&quot;list-group-item-text&quot;&gt;{{body}}&lt;/p&gt; &lt;/div&gt; {{else}} &lt;div class=&quot;list-group-item&quot;&gt; &lt;h4 class=&quot;list-group-item-heading&quot;&gt;No messages&lt;/h4&gt; &lt;/div&gt; {{/each}} {{/if}} &lt;form class=&quot;row&quot; ($submit)=&quot;send(%event)&quot;&gt; &lt;div class=&quot;col-sm-3&quot;&gt; &lt;input type=&quot;text&quot; class=&quot;form-control&quot; placeholder=&quot;Your name&quot; {($value)}=&quot;name&quot;/&gt; &lt;/div&gt; &lt;div class=&quot;col-sm-6&quot;&gt; &lt;input type=&quot;text&quot; class=&quot;form-control&quot; placeholder=&quot;Your message&quot; {($value)}=&quot;body&quot;/&gt; &lt;/div&gt; &lt;div class=&quot;col-sm-3&quot;&gt; &lt;input type=&quot;submit&quot; class=&quot;btn btn-primary btn-block&quot; value=&quot;Send&quot;/&gt; &lt;/div&gt; &lt;/form&gt; &lt;/script&gt; &lt;script src=&quot;https://code.jquery.com/jquery-2.2.4.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://unpkg.com/can/dist/global/can.js&quot;&gt;&lt;/script&gt; &lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.js&quot;&gt;&lt;/script&gt; &lt;/body&gt; &lt;/html&gt; </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: &quot;number&quot;, name: &quot;string&quot;, body: &quot;string&quot;, created_at: &quot;date&quot; }); Message.List = can.DefineList.extend({ &quot;#&quot;: 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: &quot;string&quot;, body: &quot;string&quot;, send: function(event) { event.preventDefault(); new Message({ name: this.name, body: this.body }).save().then(function(){ this.body = &quot;&quot;; }.bind(this)); } }); can.Component.extend({ tag: &quot;chat-messages&quot;, ViewModel: ChatMessagesVM, view: can.stache.from(&quot;chat-messages-template&quot;) }); var AppVM = can.DefineMap.extend({ page: &quot;string&quot;, message: { type: &quot;string&quot;, value: &quot;Chat Home&quot;, serialize: false }, addExcitement: function(){ this.message = this.message + &quot;!&quot;; } }); var appVM = new AppVM(); can.route.data = appVM; can.route(&quot;{page}&quot;,{page: &quot;home&quot;}); 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: &quot;number&quot;, name: &quot;string&quot;, body: &quot;string&quot;, created_at: &quot;date&quot; }); Message.List = can.DefineList.extend({ &quot;#&quot;: 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