Skip to content
This repository
Browse code

Bringing compiled HTML up to date.

  • Loading branch information...
commit ef07ed66c3f13c0068d4d1b6fe9af7806e3405e6 1 parent d7bdf65
Addy Osmani authored December 24, 2012

Showing 2 changed files with 323 additions and 5 deletions. Show diff stats Hide diff stats

  1. 326  index.html
  2. 2  index.md
326  index.html
@@ -196,6 +196,11 @@
196 196
 <li><a href="#configuration-options">Configuration options</a></li>
197 197
 <li><a href="#modularizing-our-models-views-and-collections">Modularizing our models, views and collections</a></li>
198 198
 </ul></li>
  199
+<li><a href="#route-based-module-loading">Route based module loading</a><ul>
  200
+<li><a href="#json-based-module-configuration">JSON based module configuration</a></li>
  201
+<li><a href="#module-loader-router">Module loader Router</a></li>
  202
+<li><a href="#using-nodejs-to-handle-pushstate">Using NodeJS to handle pushState</a></li>
  203
+</ul></li>
199 204
 <li><a href="#decoupling-backbone-with-the-mediator-and-facade-patterns">Decoupling Backbone with the Mediator and Facade Patterns</a><ul>
200 205
 <li><a href="#summary-3">Summary</a></li>
201 206
 <li><a href="#exercise">Exercise</a></li>
@@ -235,6 +240,14 @@
235 240
 <li><a href="#implementation-notes">Implementation notes:</a></li>
236 241
 </ul></li>
237 242
 <li><a href="#plugins">Plugins</a></li>
  243
+<li><a href="#thorax">Thorax</a><ul>
  244
+<li><a href="#hello-world">Hello World</a></li>
  245
+<li><a href="#embedding-child-views">Embedding child views</a></li>
  246
+<li><a href="#view-helpers">View helpers</a></li>
  247
+<li><a href="#collection-helper">collection helper</a></li>
  248
+<li><a href="#custom-html-data-attributes">Custom HTML data attributes</a></li>
  249
+<li><a href="#thorax-resources">Thorax Resources</a></li>
  250
+</ul></li>
238 251
 </ul></li>
239 252
 <li><a href="#mobile-applications">Mobile Applications</a><ul>
240 253
 <li><a href="#backbone-jquery-mobile">Backbone &amp; jQuery Mobile</a><ul>
@@ -534,14 +547,14 @@ <h3 id="views"><a href="#TOC">Views</a></h3>
534 547
 <h3 id="controllers"><a href="#TOC">Controllers</a></h3>
535 548
 <p>Controllers are an intermediary between models and views which are classically responsible for two tasks: they both update the view when the model changes and update the model when the user manipulates the view.</p>
536 549
 <p>In our Todo application, a controller would be responsible for handling changes the user made in the edit view for a particular todo, updating a specific todo model when a user has finished editing.</p>
537  
-<p>It’s with controllers that most JavaScript MVC frameworks depart from this interpretation of the MVC pattern. The reasons for this vary, but in my opinion, Javascript framework authors likely initially looked at server-side interpretations of MVC (such as Ruby on Rails), realized that that approach didn’t translate 1:1 on the client-side, and so re-interpreted the C in MVC to solve their state management problem. This was a clever approach, but it can make it hard for developers coming to MVC for the first time to understand both the classical MVC pattern and the <q>proper</q> role of controllers in other non-Javascript frameworks.</p>
  550
+<p>It’s with controllers that most JavaScript MVC frameworks depart from this interpretation of the MVC pattern. The reasons for this vary, but in my opinion, Javascript framework authors likely initially looked at server-side interpretations of MVC (such as Ruby on Rails), realized that that approach didn’t translate 1:1 on the client-side, and so re-interpreted the C in MVC to solve their state management problem. This was a clever approach, but it can make it hard for developers coming to MVC for the first time to understand both the classical MVC pattern and the <q>proper</q> role of controllers in other JavaScript frameworks.</p>
538 551
 <p>So does Backbone.js have Controllers? Not really. Backbone’s Views typically contain <q>controller</q> logic, and Routers (discussed below) are used to help manage application state, but neither are true Controllers according to classical MVC.</p>
539 552
 <p>In this respect, contrary to what might be mentioned in the official documentation or in blog posts, Backbone is neither a truly MVC/MVP nor MVVM framework. It’s in fact better to see it a member of the MV* family which approaches architecture in its own way. There is of course nothing wrong with this, but it is important to distinguish between classical MVC and MV* should you be relying on discussions of MVC to help with your Backbone projects.</p>
540 553
 <h3 id="controllers-in-spine.js-vs-backbone.js"><a href="#TOC">Controllers in Spine.js vs Backbone.js</a></h3>
541 554
 <p><strong>Spine.js</strong></p>
542 555
 <p>We now know that controllers are traditionally responsible for updating the view when the model changes (and similarly the model when the user updates the view). Since Backbone doesn’t have its <strong>own</strong> explicit controllers, it’s useful to review the controller from another MVC framework to appreciate the difference in implementations. Let’s take a look at <a href="http://spinejs.com/">Spine.js</a>:</p>
543 556
 <p>In this example, we’re going to have a controller called <code>TodoController</code> which would be in charge of individual todos in the application. It will ensure that when the view updates (e.g a user edited the todo) the corresponding model does too.</p>
544  
-<p>(Note: We won’t be delving heavily into Spine.js beyond this example, but it’s worth looking at it to learn more about Javascript frameworks in general.)</p>
  557
+<p>(Note: We won’t be delving heavily into Spine.js beyond this example, but it’s worth looking at it to learn more about JavaScript frameworks in general.)</p>
545 558
 <pre class="sourceCode javascript"><code class="sourceCode javascript"><span class="co">// Controllers in Spine are created by inheriting from Spine.Controller</span>
546 559
 
547 560
 <span class="kw">var</span> TodoController = <span class="kw">Spine.Controller</span>.<span class="fu">sub</span>({
@@ -4010,11 +4023,29 @@ <h4 id="nesting-what-is-the-best-approach-for-rendering-and-appending-sub-views-
4010 4023
         <span class="kw">this</span>.$<span class="fu">el</span>.<span class="fu">html</span>(template);
4011 4024
     }
4012 4025
 });</code></pre>
  4026
+<p>If multiple views need to be nested at particular locations in a template, a hash of child views indexed by child view cids’ should be created. In the template, use a custom HTML attribute named <code>data-view-cid</code> to create placeholder elements for each view to embed. Once the template has been rendered and it’s output appeneded to the parent view’s <code>$el</code>, each placeholder can be queried for and replaced with the child view’s <code>el</code>.</p>
  4027
+<p>A sample implementation containing a single child view:</p>
  4028
+<pre class="sourceCode javascript"><code class="sourceCode javascript">
  4029
+<span class="kw">var</span> OuterView = <span class="kw">Backbone.View</span>.<span class="fu">extend</span>({
  4030
+    <span class="dt">initialize</span>: <span class="kw">function</span>() {
  4031
+        <span class="kw">this</span>.<span class="fu">children</span> = {};
  4032
+        <span class="kw">var</span> child = <span class="kw">new</span> <span class="kw">Backbone</span>.<span class="fu">View</span>();
  4033
+        <span class="kw">this</span>.<span class="fu">children</span>[<span class="kw">child</span>.<span class="fu">cid</span>] = child;
  4034
+    },
  4035
+
  4036
+    <span class="dt">render</span>: <span class="kw">function</span>() {
  4037
+        <span class="kw">this</span>.$<span class="fu">el</span>.<span class="fu">html</span>(<span class="ch">&#39;&lt;div data-view-cid=&quot;&#39;</span> + <span class="kw">this</span>.<span class="fu">child</span>.<span class="fu">cid</span> + <span class="ch">&#39;&quot;&gt;&lt;/div&gt;&#39;</span>);        
  4038
+        <span class="kw">_</span>.<span class="fu">each</span>(<span class="kw">this</span>.<span class="fu">children</span>, <span class="kw">function</span>(view, cid) {
  4039
+            <span class="kw">this</span>.$(<span class="ch">&#39;[data-view-cid=&quot;&#39;</span> + cid + <span class="ch">&#39;&quot;]&#39;</span>).<span class="fu">replaceWith</span>(<span class="kw">view</span>.<span class="fu">el</span>);
  4040
+        }, <span class="kw">this</span>);
  4041
+    }
  4042
+};</code></pre>
4013 4043
 <p>Generally speaking, more developers opt for the first solution as:</p>
4014 4044
 <ul>
4015 4045
 <li>The majority of their views may already rely on being in the DOM in their render() method</li>
4016 4046
 <li>When the OuterView is re-rendered, views don’t have to be re-initialized where re-initialization has the potential to cause memory leaks and issues with existing bindings</li>
4017 4047
 </ul>
  4048
+<p><a href="#marionette">Marionette</a> and <a href="#thorax">Thorax</a> provide logic for nesting views, and rendering collections where each item has an associated view. Marionette provides APIs in JavaScript while Thorax provides APIs via Handlebars template helpers.</p>
4018 4049
 <p>(Thanks to <a href="http://stackoverflow.com/users/299189/lukas">Lukas</a> and <a href="http://stackoverflow.com/users/154765/ian-storm-taylor">Ian Taylor</a> for these tips).</p>
4019 4050
 <h4 id="what-is-the-best-way-to-manage-models-in-nested-views"><a href="#TOC">What is the best way to manage models in nested Views?</a></h4>
4020 4051
 <p>In order to reach attributes on related models in a nested setup, the models involved need to have some prior knowledge about which models this refers to. Backbone.js doesn’t implicitly handle relations or nesting, meaning it’s up to us to ensure models have a knowledge of each other.</p>
@@ -4399,7 +4430,7 @@ <h2 id="introduction-1"><a href="#TOC">Introduction</a></h2>
4399 4430
 <p>Unlike some more traditional programming languages however, the current iteration of JavaScript (ECMA-262) doesn’t provide developers with the means to import such modules of code in a clean, organized manner. It’s one of the concerns with specifications that haven’t required great thought until more recent years where the need for more organized JavaScript applications became apparent.</p>
4400 4431
 <p>Instead, developers at present are left to fall back on variations of the module or object literal patterns. With many of these, module scripts are strung together in the DOM with namespaces being described by a single global object where it’s still possible to incur naming collisions in your architecture. There’s also no clean way to handle dependency management without some manual effort or third party tools.</p>
4401 4432
 <p>Whilst native solutions to these problems will be arriving in ES Harmony, the good news is that writing modular JavaScript has never been easier and you can start doing it today.</p>
4402  
-<p>In this next part of the book, we’re going to look at how to use AMD modules and RequireJS for cleanly wrapping units of code in your application into manageable modules.</p>
  4433
+<p>In this next part of the book, we’re going to look at how to use AMD modules and RequireJS for cleanly wrapping units of code in your application into manageable modules, and an alternate approach using routes to determine when modules are loaded.</p>
4403 4434
 <h2 id="organizing-modules-with-requirejs-and-amd"><a href="#TOC">Organizing modules with RequireJS and AMD</a></h2>
4404 4435
 <p>In case you haven’t used it before, <a href="http://requirejs.org">RequireJS</a> is a popular script loader written by James Burke - a developer who has been quite instrumental in helping shape the AMD module format, which we’ll discuss more shortly. Some of RequireJS’s capabilities include helping to load multiple script files, helping define modules with or without dependencies and loading in non-script dependencies such as text files.</p>
4405 4436
 <p>So, why use RequireJS with Backbone? Although Backbone is excellent when it comes to providing a sanitary structure to your applications, there are a few key areas where some additional help could be used:</p>
@@ -5102,6 +5133,100 @@ <h3 id="modularizing-our-models-views-and-collections"><a href="#TOC">Modularizi
5102 5133
 <p>The rest of the source for the Todo app mainly consists of code for handling user and application events, but that rounds up most of the core concepts for this practical.</p>
5103 5134
 <p>To see how everything ties together, feel free to grab the source by cloning this repo or browse it <a href="https://github.com/addyosmani/backbone-fundamentals/tree/master/practicals/modular-todo-app">online</a> to learn more. I hope you find it helpful!.</p>
5104 5135
 <p><strong>Note:</strong> While this first practical doesn’t use a build profile as outlined in the chapter on using the RequireJS optimizer, we will be using one in the section on building mobile Backbone applications.</p>
  5136
+<h2 id="route-based-module-loading"><a href="#TOC">Route based module loading</a></h2>
  5137
+<p>This section will discuss a route based approach to module loading as implemented in <a href="http://walmartlabs.github.com/lumbar">Lumbar</a> by Kevin Decker. Like RequireJS, Lumbar is also a modular build system, but the pattern it implements for loading routes may be used with any build system.</p>
  5138
+<p>The specifics of the Lumbar build tool are not discussed in this book. To see a complete Lumbar based project with the loader and build system see <a href="http://walmartlabs.github.com/thorax">Thorax</a> which provides boilerplate projects for various environments including Lumbar.</p>
  5139
+<h3 id="json-based-module-configuration"><a href="#TOC">JSON based module configuration</a></h3>
  5140
+<p>RequireJS defines dependencies per file, while Lumbar defines a list of files for each module in a central JSON configuration file, outputting a single JavaScript file for each defined module. Lumbar requires that each module (except the base module) define a single router and a list of routes. An example file might look like:</p>
  5141
+<pre><code> {
  5142
+    &quot;modules&quot;: {
  5143
+        &quot;base&quot;: {
  5144
+            &quot;scripts&quot;: [
  5145
+                &quot;js/lib/underscore.js&quot;,
  5146
+                &quot;js/lib/backbone.js&quot;,
  5147
+                &quot;etc&quot;
  5148
+            ]
  5149
+        },
  5150
+        &quot;pages&quot;: {
  5151
+            &quot;scripts&quot;: [
  5152
+                &quot;js/routers/pages.js&quot;,
  5153
+                &quot;js/views/pages/index.js&quot;,
  5154
+                &quot;etc&quot;
  5155
+            ],
  5156
+            &quot;routes&quot;: {
  5157
+                &quot;&quot;: &quot;index&quot;,
  5158
+                &quot;contact&quot;: &quot;contact&quot;
  5159
+            }
  5160
+        }
  5161
+    }
  5162
+}</code></pre>
  5163
+<p>Every JavaScript file defined in a module will have a <code>module</code> object in scope which contains the <code>name</code> and <code>routes</code> for the module. In <code>js/routers/pages.js</code> we could define a Backbone router for our <code>pages</code> module like so:</p>
  5164
+<pre><code>new (Backbone.Router.extend({
  5165
+    routes: module.routes,
  5166
+    index: function() {},
  5167
+    contact: function() {}
  5168
+}));</code></pre>
  5169
+<h3 id="module-loader-router"><a href="#TOC">Module loader Router</a></h3>
  5170
+<p>A little used feature of <code>Backbone.Router</code> is it’s ability to create multiple routers that listen to the same set of routes. Lumbar uses this feature to create a router that listens to all routes in the application. When a route is matched, this master router checks to see if the needed module is loaded. If the module is already loaded, then the master router takes no action and the router defined by the module will handle the route. If the needed module has not yet been loaded, it will be loaded, then <code>Backbone.history.loadUrl</code> will be called. This reloads the route, causes the master router to take no further action and the router defined in the freshly loaded module to respond.</p>
  5171
+<p>A sample implementation is provided below. The <code>config</code> object would need to contain the data from our sample configuration JSON file above, and the <code>loader</code> object would need to implement <code>isLoaded</code> and <code>loadModule</code> methods. Note that Lumbar provides all of these implementations, the examples are provided to create your own implementation.</p>
  5172
+<pre><code>// Create an object that will be used as the prototype
  5173
+// for our master router
  5174
+var handlers = {
  5175
+    routes: {}
  5176
+};
  5177
+
  5178
+_.each(config.modules, function(module, moduleName) {
  5179
+    if (module.routes) {
  5180
+        // Generate a loading callback for the module
  5181
+        var callbackName = &quot;loader_&quot; moduleName;
  5182
+        handlers[callbackName] = function() {
  5183
+            if (loader.isLoaded(moduleName)) {
  5184
+                // Do nothing if the module is loaded
  5185
+                return;
  5186
+            } else {
  5187
+                //the module needs to be loaded
  5188
+                loader.loadModule(moduleName, function() {
  5189
+                    // Module is loaded, reloading the route
  5190
+                    // will trigger callback in the module&#39;s
  5191
+                    // router
  5192
+                    Backbone.history.loadUrl();
  5193
+                });
  5194
+            }
  5195
+        };
  5196
+        // Each route in the module should trigger the
  5197
+        // loading callback
  5198
+        _.each(module.routes, function(methodName, route) {
  5199
+            handlers.routes[route] = callbackName;
  5200
+        });
  5201
+    }
  5202
+});
  5203
+
  5204
+// Create the master router
  5205
+new (Backbone.Router.extend(handlers));</code></pre>
  5206
+<h3 id="using-nodejs-to-handle-pushstate"><a href="#TOC">Using NodeJS to handle pushState</a></h3>
  5207
+<p><code>window.history.pushState</code> support (serving Backbone routes without a hashtag) requires that the server be aware of what URLs your Backbone application will handle, since the user should be able to enter the app at any of those routes (or hit reload after navigating to a pushState URL).</p>
  5208
+<p>Another advantage to defining all routes in a single location is that the same JSON configuration file provided above could be loaded by the server, listening to each route. A sample implementation in NodeJS and Express:</p>
  5209
+<pre><code>var fs = require(&#39;fs&#39;),
  5210
+    _ = require(&#39;underscore&#39;),
  5211
+    express = require(&#39;express&#39;),
  5212
+    server = express.createServer(),
  5213
+    config = JSON.parse(fs.readFileSync(&#39;path/to/config.json&#39;));
  5214
+
  5215
+_.each(config.modules, function(module, moduleName) {
  5216
+    if (module.routes) {
  5217
+        _.each(module.routes, function(methodName, route) {
  5218
+            server.get(route, function(req, res) {
  5219
+                  res.sendFile(&#39;public/index.html&#39;);
  5220
+            });
  5221
+        });
  5222
+    }
  5223
+});</code></pre>
  5224
+<p>This assumes that index.html will be serving out your Backbone application. The <code>Backbone.History</code> object can handle the rest of the routing logic as long as a <code>root</code> option is specified. A sample configuration for a simple application that lives at the root might look like:</p>
  5225
+<pre><code>Backbone.history || (Backbone.history = new Backbone.History());
  5226
+Backbone.history.start({
  5227
+  pushState: true,
  5228
+  root: &#39;/&#39;
  5229
+});</code></pre>
5105 5230
 <h2 id="decoupling-backbone-with-the-mediator-and-facade-patterns"><a href="#TOC">Decoupling Backbone with the Mediator and Facade Patterns</a></h2>
5106 5231
 <p>In this section we’ll discuss applying some of the concepts I cover in my article on <a href="http://addyosmani.com/largescalejavascript">Large-scale JavaScript Application development</a> to Backbone.</p>
5107 5232
 <p><em>After, you may be interested in taking a look At <a href="http://github.com/addyosmani/aura">Aura</a> - my popular widget-based Backbone.js extension framework based on many of the concepts we will be covering in this section.</em></p>
@@ -5112,7 +5237,7 @@ <h3 id="summary-3"><a href="#TOC">Summary</a></h3>
5112 5237
 <li><strong>Supports module-level security</strong>: whereby modules are only able to execute behavior they’ve been permitted to. Application security is an area which is often overlooked in JavaScript applications, but can be quite easily implemented in a flexible manner.</li>
5113 5238
 <li><strong>Supports failover</strong>: allowing an application continuing to function even if particular modules fail. The typical example I give of this is the GMail chat widget. Imagine being able to build applications in a way that if one widget on the page fails (e.g chat), the rest of your application (mail) can continue to function without being affected.</li>
5114 5239
 </ul>
5115  
-<p>This is an architecture which has been implemented by a number of different companies in the past, including Yahoo! (for their modularized homepage - which Nicholas Zakas has <a href="http://www.youtube.com/watch?v=vXjVFPosQHw">spoken</a> about) and AOL for some of our upcoming projects.</p>
  5240
+<p>This is an architecture which has been implemented by a number of different companies in the past, including Yahoo! (for their modularized homepage.</p>
5116 5241
 <p>The three design patterns that make this architecture possible are the:</p>
5117 5242
 <ul>
5118 5243
 <li><strong>Module pattern</strong>: used for encapsulating unique blocks of code, where functions and variables can be kept either public or private. (<q>private</q> in the simulation of privacy sense, as of course don’t have true privacy in JavaScript)</li>
@@ -6324,6 +6449,197 @@ <h2 id="plugins"><a href="#TOC">Plugins</a></h2>
6324 6449
     <span class="dt">initialize</span>: <span class="kw">function</span>(){
6325 6450
       <span class="kw">this</span>.<span class="fu">useDiacriticsPlugin</span> = <span class="kw">true</span>; <span class="co">// use diacritics plugin if available</span>
6326 6451
     ...</code></pre>
  6452
+<h2 id="thorax"><a href="#TOC">Thorax</a></h2>
  6453
+<p><em>By Ryan Eastridge &amp; Addy Osmani</em></p>
  6454
+<p>Part of Backbone’s appeal is that it provides structure but is generally un-opionated, in particular when it comes to views. Thorax makes an opinionated decision to use Handlebars as it’s templating solution. Some of the patterns found in Marionette are found in Thorax as well. Marionette exposes most of these patterns as JavaScript APIs while in Thorax they are often exposed as template helpers. This chapter assumes the reader has knowledge of Handlebars.</p>
  6455
+<p>Thorax was created by Ryan Eastridge and Kevin Decker to create Walmart’s mobile web application. This chapter is limited to Thorax’s templating features and patterns implemented in Thorax that you can utilize in your application regardless of wether you choose to adopt Thorax. To learn more about other features implemented in Thorax and to download boilerplate projects visit the <a href="http://walmartlabs.github.com/thorax">Thorax website</a>.</p>
  6456
+<h3 id="hello-world"><a href="#TOC">Hello World</a></h3>
  6457
+<p><code>Thorax.View</code> differs from <code>Backbone.View</code> in that there is no <code>options</code> object. All arguments passed to the constructor become properties of the view, which in turn become available to the <code>template</code>:</p>
  6458
+<pre class="sourceCode javascript"><code class="sourceCode javascript">    <span class="kw">var</span> view = <span class="kw">new</span> <span class="kw">Thorax</span>.<span class="fu">View</span>({
  6459
+        <span class="dt">greeting</span>: <span class="ch">&#39;Hello&#39;</span>,
  6460
+        <span class="dt">template</span>: <span class="ch">&#39;{{greeting}} World!&#39;</span>
  6461
+    });
  6462
+    <span class="kw">view</span>.<span class="fu">render</span>();
  6463
+    $(<span class="ch">&#39;body&#39;</span>).<span class="fu">append</span>(<span class="kw">view</span>.<span class="fu">el</span>);</code></pre>
  6464
+<p>In most examples in this chapter a <code>template</code> property will be specified. In larger projects including the boilerplate projects provided on the Thorax website a <code>name</code> property would instead be used and a <code>template</code> of the same file name in your project would automatically be assigned to the view.</p>
  6465
+<p>If a <code>model</code> is set on a view, it’s attributes also become available to the template:</p>
  6466
+<pre><code>var view = new Thorax.View({
  6467
+    model: new Thorax.Model({key: &#39;value&#39;}),
  6468
+    template: &#39;{{key}}&#39;
  6469
+});</code></pre>
  6470
+<h3 id="embedding-child-views"><a href="#TOC">Embedding child views</a></h3>
  6471
+<p>The view helper allows you to embed other views within a view. Child views can be specified as properties of the view:</p>
  6472
+<pre class="sourceCode javascript"><code class="sourceCode javascript">    <span class="kw">var</span> parent = <span class="kw">new</span> <span class="kw">Thorax</span>.<span class="fu">View</span>({
  6473
+        <span class="dt">child</span>: <span class="kw">new</span> <span class="kw">Thorax</span>.<span class="fu">View</span>(...),
  6474
+        <span class="dt">template</span>: <span class="ch">&#39;{{view child}}&#39;</span>
  6475
+    });</code></pre>
  6476
+<p>Or the name of a child view to initialize (and any optional properties to pass). In this case the child view must have previously been created with <code>extend</code> and a <code>name</code> property:</p>
  6477
+<pre class="sourceCode javascript"><code class="sourceCode javascript">    <span class="kw">var</span> ChildView = <span class="kw">Thorax.View</span>.<span class="fu">extend</span>({
  6478
+        <span class="dt">name</span>: <span class="ch">&#39;child&#39;</span>,
  6479
+        <span class="dt">template</span>: ...
  6480
+    });
  6481
+
  6482
+    <span class="kw">var</span> parent = <span class="kw">new</span> <span class="kw">Thorax</span>.<span class="fu">View</span>({
  6483
+        <span class="dt">template</span>: <span class="ch">&#39;{{view &quot;child&quot; key=&quot;value&quot;}}&#39;</span>
  6484
+    });</code></pre>
  6485
+<p>The view helper may also be used as a block helper, in which case the block will be assigned as the <code>template</code> property of the child view:</p>
  6486
+<pre class="handlebars"><code>    {{#view child}}
  6487
+        child will have this block
  6488
+        set as it&#39;s template property
  6489
+    {{/view}}</code></pre>
  6490
+<p>Handlebars is a string based, while <code>Backbone.View</code> instances have a DOM <code>el</code>. Since we are mixing metaphors, the embedding of views works via a placeholder mechanism where the <code>view</code> helper in this case adds the view passed to the helper to a hash of <code>children</code>, then injects placeholder HTML into the template such as:</p>
  6491
+<pre class="sourceCode html"><code class="sourceCode html">    <span class="kw">&lt;div</span><span class="ot"> data-view-placeholder-cid=</span><span class="st">&quot;view2&quot;</span><span class="kw">&gt;&lt;/div&gt;</span></code></pre>
  6492
+<p>Then once the parent view is rendered, we walk the DOM in search of all the placeholders we created, replacing them with the child views’ <code>el</code>s:</p>
  6493
+<pre class="sourceCode javascript"><code class="sourceCode javascript">    <span class="kw">this</span>.$<span class="fu">el</span>.<span class="fu">find</span>(<span class="ch">&#39;[data-view-placeholder-cid]&#39;</span>).<span class="fu">forEach</span>(<span class="kw">function</span>(el) {
  6494
+        <span class="kw">var</span> cid = <span class="kw">el</span>.<span class="fu">getAttribute</span>(<span class="ch">&#39;data-view-placeholder-cid&#39;</span>),
  6495
+            view = <span class="kw">this</span>.<span class="fu">children</span>[cid];
  6496
+        <span class="kw">view</span>.<span class="fu">render</span>();
  6497
+        $(el).<span class="fu">replaceWith</span>(<span class="kw">view</span>.<span class="fu">el</span>);
  6498
+    }, <span class="kw">this</span>);</code></pre>
  6499
+<h3 id="view-helpers"><a href="#TOC">View helpers</a></h3>
  6500
+<p>One of the most useful constructs in Thorax is <code>Handlebars.registerViewHelper</code> (which differs from <code>Handlebars.registerHelper</code>). This method will register a new block helper that will create and embed a <code>HelperView</code> instance with it’s <code>template</code> set to the captured block. A <code>HelperView</code> instance is different from that of a regular child view in that it’s context will be that of the parent’s in the template. Like other child views it will have a <code>parent</code> property set to that of the declaring view. Many of the built in helpers in Thorax including the <code>collection</code> helper are created in this manner.</p>
  6501
+<p>A simple example would be an <code>on</code> helper that re-rendered the generated <code>HelperView</code> instance each time an event was triggered on the declaring / parent view:</p>
  6502
+<pre><code>Handlebars.registerViewHelper(&#39;on&#39;, function(eventName, helperView) {
  6503
+    helperView.parent.on(eventName, function() {
  6504
+        helperView.render();
  6505
+    });
  6506
+});</code></pre>
  6507
+<p>An example use of this would be to have a counter that would incriment each time a button was clicked. This example makes use of the <code>button</code> helper in Thorax which simply makes a button that calls a method when clicked:</p>
  6508
+<pre class="handlebars"><code>    {{#on &quot;incrimented&quot;}}{{i}}{/on}}
  6509
+    {{#button trigger=&quot;incrimented&quot;}}Add{{/button}}</code></pre>
  6510
+<p>And the corresponding view class:</p>
  6511
+<pre class="sourceCode javascript"><code class="sourceCode javascript">    <span class="kw">new</span> <span class="kw">Thorax</span>.<span class="fu">View</span>({
  6512
+        <span class="dt">events</span>: {
  6513
+            <span class="dt">incrimented</span>: <span class="kw">function</span>() {
  6514
+                ++<span class="kw">this</span>.<span class="fu">i</span>;
  6515
+            }
  6516
+        },
  6517
+        <span class="dt">initialize</span>: <span class="kw">function</span>() {
  6518
+            <span class="kw">this</span>.<span class="fu">i</span> = <span class="dv">0</span>;
  6519
+        },
  6520
+        <span class="dt">template</span>: ...
  6521
+    });</code></pre>
  6522
+<h3 id="collection-helper"><a href="#TOC">collection helper</a></h3>
  6523
+<p>The <code>collection</code> helper creates and embeds a <code>CollectionView</code> instance, creating a view for each item in a collection, updating when items are added, removed or changed in the collection. The simplest usage of the helper would look like:</p>
  6524
+<pre class="handlebars"><code>    {{#collection kittens}}
  6525
+      &lt;li&gt;{{name}}&lt;/li&gt;
  6526
+    {{/collection}}</code></pre>
  6527
+<p>And the corresponding view:</p>
  6528
+<pre class="sourceCode javascript"><code class="sourceCode javascript">    <span class="kw">new</span> <span class="kw">Thorax</span>.<span class="fu">View</span>({
  6529
+      <span class="dt">kittens</span>: <span class="kw">new</span> <span class="kw">Thorax</span>.<span class="fu">Collection</span>(...),
  6530
+      <span class="dt">template</span>: ...
  6531
+    });</code></pre>
  6532
+<p>The block in this case will be assigned as the <code>template</code> for each item view created, and the context will be the <code>attributes</code> of the given model. This helper accepts options that can be arbitrary HTML attributes, a <code>tag</code> option to specify the type of tag containing the collection, or any of the following:</p>
  6533
+<ul>
  6534
+<li><code>item-template</code> - A template to display for each model. If a block is specified it will become the item-template</li>
  6535
+<li><code>item-view</code> - A view class to use when each item view is created</li>
  6536
+<li><code>empty-template</code> - A template to display when the collection is empty. If an inverse / else block is specified it will become the empty-template</li>
  6537
+<li><code>empty-view</code> - A view to display when the collection is empty</li>
  6538
+</ul>
  6539
+<p>Options and blocks can be used in combination, in this case creating a <code>KittenView</code> class with a <code>template</code> set to the captured block for each kitten in the collection:</p>
  6540
+<pre class="handlebars"><code>    {{#collection kittens item-view=&quot;KittenView&quot; tag=&quot;ul&quot;}}
  6541
+      &lt;li&gt;{{name}}&lt;/li&gt;
  6542
+    {{else}}
  6543
+      &lt;li&gt;No kittens!&lt;/li&gt;
  6544
+    {{/collection}}</code></pre>
  6545
+<p>Note that multiple collections can be used per view, and collections can be nested. This is useful when there are models that contain collections that contain models that contain…</p>
  6546
+<pre class="handlebars"><code>    {{#collection kittens}}
  6547
+      &lt;h2&gt;{{name}}&lt;/h2&gt;
  6548
+      &lt;p&gt;Kills:&lt;/p&gt;
  6549
+      {{#collection miceKilled tag=&quot;ul&quot;}}
  6550
+        &lt;li&gt;{{name}}&lt;/li&gt;
  6551
+      {{/collection}}
  6552
+    {{/collection}}</code></pre>
  6553
+<h3 id="custom-html-data-attributes"><a href="#TOC">Custom HTML data attributes</a></h3>
  6554
+<p>Thorax makes heavy use of custom data attributes to operate. While some make sense only within the context of Thorax, several are quite useful to have in any Backbone project for writing other functions against, or for general debugging. In order to add some to your views in non Thorax projects, override the <code>setElement</code> method in your base view class:</p>
  6555
+<pre class="sourceCode javascript"><code class="sourceCode javascript">  <span class="kw">MyApplication</span>.<span class="fu">View</span> = <span class="kw">Backbone.View</span>.<span class="fu">extend</span>({
  6556
+    <span class="dt">setElement</span>: <span class="kw">function</span>() {
  6557
+        <span class="kw">var</span> response = <span class="kw">Backbone.View.prototype.setElement</span>.<span class="fu">apply</span>(<span class="kw">this</span>, arguments);
  6558
+        <span class="kw">this</span>.<span class="fu">name</span> &amp;&amp; <span class="kw">this</span>.$<span class="fu">el</span>.<span class="fu">attr</span>(<span class="ch">&#39;data-view-name&#39;</span>, <span class="kw">this</span>.<span class="fu">name</span>);
  6559
+        <span class="kw">this</span>.$<span class="fu">el</span>.<span class="fu">attr</span>(<span class="ch">&#39;data-view-cid&#39;</span>, <span class="kw">this</span>.<span class="fu">cid</span>);
  6560
+        <span class="kw">this</span>.<span class="fu">collection</span> &amp;&amp; <span class="kw">this</span>.$<span class="fu">el</span>.<span class="fu">attr</span>(<span class="ch">&#39;data-collection-cid&#39;</span>, <span class="kw">this</span>.<span class="fu">collection</span>.<span class="fu">cid</span>);
  6561
+        <span class="kw">this</span>.<span class="fu">model</span> &amp;&amp; <span class="kw">this</span>.$<span class="fu">el</span>.<span class="fu">attr</span>(<span class="ch">&#39;data-model-cid&#39;</span>, <span class="kw">this</span>.<span class="fu">model</span>.<span class="fu">cid</span>);
  6562
+        <span class="kw">return</span> response;
  6563
+    }
  6564
+  });</code></pre>
  6565
+<p>In addition making your application more immediately comprehensible in the inspector, it’s now possible to extend jQuery / Zepto with functions to lookup the closest view, model or collection to a given element. In order to make it work save references to each view created in your base view class by overriding the <code>_configure</code> method:</p>
  6566
+<pre class="sourceCode javascript"><code class="sourceCode javascript">    <span class="kw">MyApplication</span>.<span class="fu">View</span> = <span class="kw">Backbone.View</span>.<span class="fu">extend</span>({
  6567
+        <span class="dt">_configure</span>: <span class="kw">function</span>() {
  6568
+            <span class="kw">Backbone.View.prototype._configure</span>.<span class="fu">apply</span>(<span class="kw">this</span>, arguments);
  6569
+            <span class="kw">Thorax</span>._<span class="fu">viewsIndexedByCid</span>[<span class="kw">this</span>.<span class="fu">cid</span>] = cid;
  6570
+        },
  6571
+        <span class="dt">dispose</span>: <span class="kw">function</span>() {
  6572
+            <span class="kw">Backbone.View.prototype.dispose</span>.<span class="fu">apply</span>(<span class="kw">this</span>, arguments);
  6573
+            <span class="kw">delete</span> <span class="kw">Thorax</span>._<span class="fu">viewsIndexedByCid</span>[<span class="kw">this</span>.<span class="fu">cid</span>];
  6574
+        }
  6575
+    });</code></pre>
  6576
+<p>Then we can extend jQuery / Zepto:</p>
  6577
+<pre class="sourceCode javascript"><code class="sourceCode javascript">    $.<span class="fu">fn</span>.<span class="fu">view</span> = <span class="kw">function</span>() {
  6578
+        <span class="kw">var</span> el = $(<span class="kw">this</span>).<span class="fu">closest</span>(<span class="ch">&#39;[data-view-cid]&#39;</span>);
  6579
+        <span class="kw">return</span> el &amp;&amp; <span class="kw">Thorax</span>._<span class="fu">viewsIndexedByCid</span>[<span class="kw">el</span>.<span class="fu">attr</span>(<span class="ch">&#39;data-view-cid&#39;</span>)];
  6580
+    };
  6581
+
  6582
+    $.<span class="fu">fn</span>.<span class="fu">model</span> = <span class="kw">function</span>(view) {
  6583
+        <span class="kw">var</span> $this = $(<span class="kw">this</span>),
  6584
+            modelElement = $<span class="kw">this</span>.<span class="fu">closest</span>(<span class="ch">&#39;[data-model-cid]&#39;</span>),
  6585
+            modelCid = modelElement &amp;&amp; <span class="kw">modelElement</span>.<span class="fu">attr</span>(<span class="ch">&#39;[data-model-cid]&#39;</span>);
  6586
+        <span class="kw">if</span> (modelCid) {
  6587
+            <span class="kw">var</span> view = $<span class="kw">this</span>.<span class="fu">view</span>();
  6588
+            <span class="kw">return</span> view &amp;&amp; <span class="kw">view</span>.<span class="fu">model</span>;
  6589
+        }
  6590
+        <span class="kw">return</span> <span class="kw">false</span>;
  6591
+    };</code></pre>
  6592
+<p>Now instead of storing references to models randomly throughout your application to lookup when a given DOM event occurs you can use <code>$(element).model()</code>. In Thorax, this can particularly useful in conjunction with the <code>collection</code> helper which generates a view class (with a <code>model</code> property) for each <code>model</code> in the collection. An example template:</p>
  6593
+<pre class="handlebars"><code>    {{#collection kittens tag=&quot;ul&quot;}}
  6594
+      &lt;li&gt;{{name}}&lt;/li&gt;
  6595
+    {{/collection}}</code></pre>
  6596
+<p>And the corresponding view class:</p>
  6597
+<pre class="sourceCode javascript"><code class="sourceCode javascript">    <span class="kw">Thorax.View</span>.<span class="fu">extend</span>({
  6598
+      <span class="dt">events</span>: {
  6599
+        <span class="ch">&#39;click li&#39;</span>: <span class="kw">function</span>(event) {
  6600
+          <span class="kw">var</span> kitten = $(<span class="kw">event</span>.<span class="fu">target</span>).<span class="fu">model</span>();
  6601
+          <span class="kw">console</span>.<span class="fu">log</span>(<span class="ch">&#39;Clicked on &#39;</span> + <span class="kw">kitten</span>.<span class="fu">get</span>(<span class="ch">&#39;name&#39;</span>));
  6602
+        }
  6603
+      },
  6604
+      <span class="dt">kittens</span>: <span class="kw">new</span> <span class="kw">Thorax</span>.<span class="fu">Collection</span>(...),
  6605
+      <span class="dt">template</span>: ...
  6606
+    });  </code></pre>
  6607
+<p>A common anti-pattern in Backbone applications is to assign a <code>className</code> to a single view class. Consider using the <code>data-view-name</code> attribute as a CSS selector instead, saving CSS classes for things that will be used multiple times:</p>
  6608
+<pre class="sourceCode css"><code class="sourceCode css">  <span class="ch">[data-view-name=</span><span class="st">&quot;child&quot;</span><span class="ch">]</span> <span class="kw">{</span>
  6609
+
  6610
+  <span class="kw">}</span></code></pre>
  6611
+<h3 id="thorax-resources"><a href="#TOC">Thorax Resources</a></h3>
  6612
+<p>No Backbone related tutorial would be complete without a todo application. A <a href="http://todomvc.com/labs/architecture-examples/thorax/">Thorax implementation of TodoMVC</a> is available, in addition to this far simpler example composed of this single handlebars template:</p>
  6613
+<pre class="handlebars"><code>  {{#collection todos tag=&quot;ul&quot;}}
  6614
+    &lt;li{{#if done}} class=&quot;done&quot;{{/if}}&gt;
  6615
+      &lt;input type=&quot;checkbox&quot; name=&quot;done&quot;{{#if done}} checked=&quot;checked&quot;{{/if}}&gt;
  6616
+      &lt;span&gt;{{item}}&lt;/span&gt;
  6617
+    &lt;/li&gt;
  6618
+  {{/collection}}
  6619
+  &lt;form&gt;
  6620
+    &lt;input type=&quot;text&quot;&gt;
  6621
+    &lt;input type=&quot;submit&quot; value=&quot;Add&quot;&gt;
  6622
+  &lt;/form&gt;</code></pre>
  6623
+<p>and the corresponding JavaScript:</p>
  6624
+<pre class="sourceCode javascript"><code class="sourceCode javascript">  <span class="kw">var</span> todosView = <span class="kw">Thorax</span>.<span class="fu">View</span>({
  6625
+      <span class="dt">todos</span>: <span class="kw">new</span> <span class="kw">Thorax</span>.<span class="fu">Collection</span>(),
  6626
+      <span class="dt">events</span>: {
  6627
+          <span class="ch">&#39;change input[type=&quot;checkbox&quot;]&#39;</span>: <span class="kw">function</span>(event) {
  6628
+              <span class="kw">var</span> target = $(<span class="kw">event</span>.<span class="fu">target</span>);
  6629
+              <span class="kw">target</span>.<span class="fu">model</span>().<span class="fu">set</span>({<span class="dt">done</span>: !!<span class="kw">target</span>.<span class="fu">attr</span>(<span class="ch">&#39;checked&#39;</span>)});
  6630
+          },
  6631
+          <span class="ch">&#39;submit form&#39;</span>: <span class="kw">function</span>(event) {
  6632
+              <span class="kw">event</span>.<span class="fu">preventDefault</span>();
  6633
+              <span class="kw">var</span> input = <span class="kw">this</span>.$(<span class="ch">&#39;input[type=&quot;text&quot;]&#39;</span>);
  6634
+              <span class="kw">this</span>.<span class="fu">todos</span>.<span class="fu">add</span>({<span class="dt">item</span>: <span class="kw">input</span>.<span class="fu">val</span>()});
  6635
+              <span class="kw">input</span>.<span class="fu">val</span>(<span class="ch">&#39;&#39;</span>);
  6636
+          }
  6637
+      },
  6638
+      <span class="dt">template</span>: <span class="ch">&#39;...&#39;</span>
  6639
+  });
  6640
+  <span class="kw">todosView</span>.<span class="fu">render</span>();
  6641
+  $(<span class="ch">&#39;body&#39;</span>).<span class="fu">append</span>(<span class="kw">todosView</span>.<span class="fu">el</span>);</code></pre>
  6642
+<p>To see Thorax in action on a large scale website visit walmart.com on any Android or iOS device. For a complete list of resources visit the <a href="http://walmartlabs.github.com/thorax">Thorax website</a>.</p>
6327 6643
 <h1 id="mobile-applications"><a href="#TOC">Mobile Applications</a></h1>
6328 6644
 <h2 id="backbone-jquery-mobile"><a href="#TOC">Backbone &amp; jQuery Mobile</a></h2>
6329 6645
 <h3 id="resolving-the-routing-conflicts"><a href="#TOC">Resolving the routing conflicts</a></h3>
@@ -7846,6 +8162,8 @@ <h1 id="resources"><a href="#TOC">Resources</a></h1>
7846 8162
 <p>Whilst what we get with Backbone out of the box can be terribly useful, there are some equally beneficial add-ons that can help simplify our development process. These include:</p>
7847 8163
 <ul>
7848 8164
 <li><a href="https://github.com/derickbailey/backbone.marionette">Backbone Marionette</a></li>
  8165
+<li><a href="http://walmartlabs.github.com/thorax">Thorax</a></li>
  8166
+<li><a href="http://walmartlabs.github.com/lumbar">Lumbar</a></li>
7849 8167
 <li><a href="https://github.com/tbranyen/backbone.layoutmanager">Backbone Layout Manager</a></li>
7850 8168
 <li><a href="https://github.com/backbone-boilerplate/backbone-boilerplate">Backbone Boilerplate</a></li>
7851 8169
 <li><a href="https://github.com/derickbailey/backbone.modelbinding">Backbone Model Binding</a></li>
2  index.md
Source Rendered
@@ -6436,7 +6436,7 @@ At a high-level, one architecture that works for such applications is something
6436 6436
 * **Supports module-level security**: whereby modules are only able to execute behavior they've been permitted to. Application security is an area which is often overlooked in JavaScript applications, but can be quite easily implemented in a flexible manner.
6437 6437
 * **Supports failover**: allowing an application continuing to function even if particular modules fail. The typical example I give of this is the GMail chat widget. Imagine being able to build applications in a way that if one widget on the page fails (e.g chat), the rest of your application (mail) can continue to function without being affected.
6438 6438
 
6439  
-This is an architecture which has been implemented by a number of different companies in the past, including Yahoo! (for their modularized homepage - which Nicholas Zakas has [spoken](http://www.youtube.com/watch?v=vXjVFPosQHw) about) and AOL for some of our upcoming projects.
  6439
+This is an architecture which has been implemented by a number of different companies in the past, including Yahoo! (for their modularized homepage.
6440 6440
 
6441 6441
 The three design patterns that make this architecture possible are the:
6442 6442
 

0 notes on commit ef07ed6

Please sign in to comment.
Something went wrong with that request. Please try again.