PageRenderTime 122ms CodeModel.GetById 3ms app.highlight 102ms RepoModel.GetById 1ms app.codeStats 1ms

/chapters/03-internals.md

https://github.com/bicccio/backbone-fundamentals
Markdown | 2021 lines | 1464 code | 557 blank | 0 comment | 0 complexity | 8978f4dca727f1f343e0e038990a4437 MD5 | raw file
   1# Backbone Basics
   2
   3In this section, you'll learn the essentials of Backbone's models, views, collections, events, and routers. This isn't by any means a replacement for the official documentation, but it will help you understand many of the core concepts behind Backbone before you start building applications using it.
   4
   5### Getting set up
   6
   7Before we dive into more code examples, let's define some boilerplate markup you can use to specify the dependencies Backbone requires. This boilerplate can be reused in many ways with little to no alteration and will allow you to run code from examples with ease.
   8
   9You can paste the following into your text editor of choice, replacing the commented line between the script tags with the JavaScript from any given example:
  10
  11```html
  12<!DOCTYPE HTML>
  13<html>
  14<head>
  15    <meta charset="UTF-8">
  16    <title>Title</title>
  17</head>
  18<body>
  19
  20<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  21<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
  22<script src="http://documentcloud.github.com/backbone/backbone-min.js"></script>
  23<script>
  24  // Your code goes here
  25</script>
  26</body>
  27</html>
  28```
  29
  30You can then save and run the file in your browser of choice, such as Chrome or Firefox. Alternatively, if you prefer working with an online code editor, [jsFiddle](http://jsfiddle.net/jnf8B/) and [jsBin](http://jsbin.com/iwiwox/1/edit) versions of this boilerplate are also available.
  31
  32Most examples can also be run directly from within the console in your browser's developer tools, assuming you've loaded the boilerplate HTML page so that Backbone and its dependencies are available for use.
  33
  34For Chrome, you can open up the DevTools via the Chrome menu in the top right hand corner: select "Tools > Developer Tools" or alternatively use the Control + Shift + I shortcut on Windows/Linux or Command + Option + I on Mac. 
  35
  36![](img/devtools.png)
  37
  38Next, switch to the Console tab, from where you can enter in and run any piece of JavaScript code by hitting the return key. You can also use the Console as a multi-line editor using the Shift + Enter shortcut on Windows, or Ctrl + Enter shortcut on Mac to move from the end of one line to the start of another.
  39 
  40
  41## Models
  42
  43Backbone models contain data for an application as well as the logic around this data. For example, we can use a model to represent the concept of a todo item including its attributes like title (todo content) and completed (current state of the todo).
  44
  45Models can be created by extending `Backbone.Model` as follows:
  46
  47```javascript
  48var Todo = Backbone.Model.extend({});
  49
  50// We can then create our own concrete instance of a (Todo) model
  51// with no values at all:
  52var todo1 = new Todo();
  53// Following logs: {}
  54console.log(JSON.stringify(todo1));
  55
  56// or with some arbitrary data:
  57var todo2 = new Todo({
  58  title: 'Check the attributes of both model instances in the console.',
  59  completed: true
  60});
  61
  62// Following logs: {"title":"Check the attributes of both model instances in the console.","completed":true}
  63console.log(JSON.stringify(todo2));
  64```
  65
  66#### Initialization
  67
  68The `initialize()` method is called when a new instance of a model is created. Its use is optional; however you'll see why it's good practice to use it below.
  69
  70```javascript
  71var Todo = Backbone.Model.extend({
  72  initialize: function(){
  73      console.log('This model has been initialized.');
  74  }
  75});
  76
  77var myTodo = new Todo();
  78// Logs: This model has been initialized.
  79```
  80
  81**Default values**
  82
  83There are times when you want your model to have a set of default values (e.g., in a scenario where a complete set of data isn't provided by the user). This can be set using a property called `defaults` in your model.
  84
  85```javascript
  86var Todo = Backbone.Model.extend({
  87  // Default todo attribute values
  88  defaults: {
  89    title: '',
  90    completed: false
  91  }
  92});
  93
  94// Now we can create our concrete instance of the model
  95// with default values as follows:
  96var todo1 = new Todo();
  97
  98// Following logs: {"title":"","completed":false}
  99console.log(JSON.stringify(todo1));
 100
 101// Or we could instantiate it with some of the attributes (e.g., with custom title):
 102var todo2 = new Todo({
 103  title: 'Check attributes of the logged models in the console.'
 104});
 105
 106// Following logs: {"title":"Check attributes of the logged models in the console.","completed":false}
 107console.log(JSON.stringify(todo2));
 108
 109// Or override all of the default attributes:
 110var todo3 = new Todo({
 111  title: 'This todo is done, so take no action on this one.',
 112  completed: true
 113});
 114
 115// Following logs: {"title":"This todo is done, so take no action on this one.","completed":true} 
 116console.log(JSON.stringify(todo3));
 117```
 118
 119#### Getters & Setters
 120
 121**Model.get()**
 122
 123`Model.get()` provides easy access to a model's attributes.
 124
 125```javascript
 126var Todo = Backbone.Model.extend({
 127  // Default todo attribute values
 128  defaults: {
 129    title: '',
 130    completed: false
 131  }
 132});
 133
 134var todo1 = new Todo();
 135console.log(todo1.get('title')); // empty string
 136console.log(todo1.get('completed')); // false
 137
 138var todo2 = new Todo({
 139  title: "Retrieved with model's get() method.",
 140  completed: true
 141});
 142console.log(todo2.get('title')); // Retrieved with model's get() method.
 143console.log(todo2.get('completed')); // true
 144```
 145
 146If you need to read or clone all of a model's data attributes, use its `toJSON()` method. This method returns a copy of the attributes as an object (not a JSON string despite its name). (When `JSON.stringify()` is passed an object with a `toJSON()` method, it stringifies the return value of `toJSON()` instead of the original object. The examples in the previous section took advantage of this feature when they called `JSON.stringify()` to log model instances.)
 147
 148```javascript
 149var Todo = Backbone.Model.extend({
 150  // Default todo attribute values
 151  defaults: {
 152    title: '',
 153    completed: false
 154  }
 155});
 156
 157var todo1 = new Todo();
 158var todo1Attributes = todo1.toJSON();
 159// Following logs: {"title":"","completed":false} 
 160console.log(todo1Attributes);
 161
 162var todo2 = new Todo({
 163  title: "Try these examples and check results in console.",
 164  completed: true
 165});
 166
 167// logs: {"title":"Try these examples and check results in console.","completed":true}
 168console.log(todo2.toJSON());
 169```
 170
 171**Model.set()**
 172
 173`Model.set()` sets a hash containing one or more attributes on the model. When any of these attributes alter the state of the model, a "change" event is triggered on it. Change events for each attribute are also triggered and can be bound to (e.g. `change:name`, `change:age`).
 174
 175```javascript
 176var Todo = Backbone.Model.extend({
 177  // Default todo attribute values
 178  defaults: {
 179    title: '',
 180    completed: false
 181  }
 182});
 183
 184// Setting the value of attributes via instantiation
 185var myTodo = new Todo({
 186  title: "Set through instantiation."
 187});
 188console.log('Todo title: ' + myTodo.get('title')); // Todo title: Set through instantiation.
 189console.log('Completed: ' + myTodo.get('completed')); // Completed: false
 190
 191// Set single attribute value at a time through Model.set():
 192myTodo.set("title", "Title attribute set through Model.set().");
 193console.log('Todo title: ' + myTodo.get('title')); // Todo title: Title attribute set through Model.set().
 194console.log('Completed: ' + myTodo.get('completed')); // Completed: false
 195
 196// Set map of attributes through Model.set():
 197myTodo.set({
 198  title: "Both attributes set through Model.set().",
 199  completed: true
 200});
 201console.log('Todo title: ' + myTodo.get('title')); // Todo title: Both attributes set through Model.set().
 202console.log('Completed: ' + myTodo.get('completed')); // Completed: true
 203```
 204
 205**Direct access**
 206
 207Models expose an `.attributes` attribute which represents an internal hash containing the state of that model. This is generally in the form of a JSON object similar to the model data you might find on the server but can take other forms.
 208
 209Setting values through the `.attributes` attribute on a model bypasses triggers bound to the model.
 210
 211Passing `{silent:true}` on change doesn't delay individual `"change:attr"` events. Instead they are silenced entirely:
 212
 213```javascript
 214var Person = new Backbone.Model();
 215Person.set({name: 'Jeremy'}, {silent: true});
 216
 217console.log(!Person.hasChanged(0));
 218// true
 219console.log(!Person.hasChanged(''));
 220// true
 221```
 222
 223Remember where possible it is best practice to use `Model.set()`, or direct instantiation as explained earlier.
 224
 225#### Listening for changes to your model
 226
 227If you want to receive a notification when a Backbone model changes you can bind a listener to the model for its change event. A convenient place to add listeners is in the `initialize()` function as shown below:
 228
 229```javascript
 230var Todo = Backbone.Model.extend({
 231  // Default todo attribute values
 232  defaults: {
 233    title: '',
 234    completed: false
 235  },
 236  initialize: function(){
 237    console.log('This model has been initialized.');
 238    this.on('change', function(){
 239        console.log('- Values for this model have changed.');
 240    });
 241  }
 242});
 243
 244var myTodo = new Todo();
 245
 246myTodo.set('title', 'The listener is triggered whenever an attribute value changes.');
 247console.log('Title has changed: ' + myTodo.get('title'));
 248
 249
 250myTodo.set('completed', true);
 251console.log('Completed has changed: ' + myTodo.get('completed'));
 252
 253myTodo.set({
 254  title: 'Changing more than one attribute at the same time only triggers the listener once.',
 255  completed: true
 256});
 257
 258// Above logs:
 259// This model has been initialized.
 260// - Values for this model have changed.
 261// Title has changed: The listener is triggered whenever an attribute value changes.
 262// - Values for this model have changed.
 263// Completed has changed: true
 264// - Values for this model have changed.
 265```
 266
 267You can also listen for changes to individual attributes in a Backbone model. In the following example, we log a message whenever a specific attribute (the title of our Todo model) is altered.
 268
 269```javascript
 270var Todo = Backbone.Model.extend({
 271  // Default todo attribute values
 272  defaults: {
 273    title: '',
 274    completed: false
 275  },
 276
 277  initialize: function(){
 278    console.log('This model has been initialized.');
 279    this.on('change:title', function(){
 280        console.log('Title value for this model has changed.');
 281    });
 282  },
 283
 284  setTitle: function(newTitle){
 285    this.set({ title: newTitle });
 286  }
 287});
 288
 289var myTodo = new Todo();
 290
 291// Both of the following changes trigger the listener:
 292myTodo.set('title', 'Check what\'s logged.');
 293myTodo.setTitle('Go fishing on Sunday.');
 294
 295// But, this change type is not observed, so no listener is triggered:
 296myTodo.set('completed', true);
 297console.log('Todo set as completed: ' + myTodo.get('completed'));
 298
 299// Above logs:
 300// This model has been initialized.
 301// Title value for this model has changed.
 302// Title value for this model has changed.
 303// Todo set as completed: true
 304```
 305
 306
 307#### Validation
 308
 309Backbone supports model validation through `model.validate()`, which allows checking the attribute values for a model prior to setting them. By default, validation occurs when the model is persisted using the `save()` method or when `set()` is called if `{validate:true}` is passed as an argument.
 310
 311```javascript
 312var Person = new Backbone.Model({name: 'Jeremy'});
 313
 314// Validate the model name
 315Person.validate = function(attrs) {
 316  if (!attrs.name) {
 317    return 'I need your name';
 318  }
 319};
 320
 321// Change the name
 322Person.set({name: 'Samuel'});
 323console.log(Person.get('name'));
 324// 'Samuel'
 325
 326// Remove the name attribute, force validation
 327Person.unset('name', {validate: true});
 328// false
 329```
 330
 331Above, we also use the `unset()` method, which removes an attribute by deleting it from the internal model attributes hash.
 332
 333Validation functions can be as simple or complex as necessary. If the attributes provided are valid, nothing should be returned from `.validate()`. If they are invalid, an error value should be returned instead. 
 334
 335Should an error be returned:
 336
 337* An `invalid` event will triggered, setting the `validationError` property on the model with the value which is returned by this method. 
 338* `.save()` will not continue and the attributes of the model will not be modified on the server.
 339
 340A more complete validation example can be seen below:
 341
 342```javascript
 343var Todo = Backbone.Model.extend({
 344  defaults: {
 345    completed: false
 346  },
 347
 348  validate: function(attribs){
 349    if(attribs.title === undefined){
 350        return "Remember to set a title for your todo.";
 351    }
 352  },
 353
 354  initialize: function(){
 355    console.log('This model has been initialized.');
 356    this.on("invalid", function(model, error){
 357        console.log(error);
 358    });
 359  }
 360});
 361
 362var myTodo = new Todo();
 363myTodo.set('completed', true, {validate: true}); // logs: Remember to set a title for your todo.
 364console.log('completed: ' + myTodo.get('completed')); // completed: false
 365```
 366
 367**Note**: the `attributes` object passed to the `validate` function represents what the attributes would be after completing the current `set()` or `save()`. This object is distinct from the current attributes of the model and from the parameters passed to the operation. Since it is created by shallow copy, it is not possible to change any Number, String, or Boolean attribute of the input within the function, but it *is* possible to change attributes in nested objects.
 368
 369An example of this (by @fivetanley) is available [here](http://jsfiddle.net/2NdDY/7/).
 370
 371
 372## Views
 373
 374Views in Backbone don't contain the HTML markup for your application; they contain the logic behind the presentation of the model's data to the user. This is usually achieved using JavaScript templating (e.g., Underscore Microtemplates, Mustache, jQuery-tmpl, etc.). A view's `render()` method can be bound to a model's `change()` event, enabling the view to instantly reflect model changes without requiring a full page refresh.
 375
 376
 377#### Creating new views
 378
 379Creating a new view is relatively straightforward and similar to creating new models. To create a new View, simply extend `Backbone.View`. We introduced the sample TodoView below in the previous chapter; now let's take a closer look at how it works:
 380
 381```javascript
 382var TodoView = Backbone.View.extend({
 383
 384  tagName:  'li',
 385
 386  // Cache the template function for a single item.
 387  todoTpl: _.template( "An example template" ),
 388
 389  events: {
 390    'dblclick label': 'edit',
 391    'keypress .edit': 'updateOnEnter',
 392    'blur .edit':   'close'
 393  },
 394
 395  // Re-render the titles of the todo item.
 396  render: function() {
 397    this.$el.html( this.todoTpl( this.model.toJSON() ) );
 398    this.input = this.$('.edit');
 399    return this;
 400  },
 401
 402  edit: function() {
 403    // executed when todo label is double clicked
 404  },
 405
 406  close: function() {
 407    // executed when todo loses focus
 408  },
 409
 410  updateOnEnter: function( e ) {
 411    // executed on each keypress when in todo edit mode,
 412    // but we'll wait for enter to get in action
 413  }
 414});
 415
 416var todoView = new TodoView();
 417
 418// log reference to a DOM element that corresponds to the view instance
 419console.log(todoView.el); // logs <li></li>
 420```
 421
 422#### What is `el`?
 423
 424The central property of a view is `el` (the value logged in the last statement of the example). What is `el` and how is it defined? 
 425
 426`el` is basically a reference to a DOM element and all views must have one. Views can use `el` to compose their element's content and then insert it into the DOM all at once, which makes for faster rendering because the browser performs the minimum required number of reflows and repaints.
 427
 428There are two ways to associate a DOM element with a view: a new element can be created for the view and subsequently added to the DOM or a reference can be made to an element which already exists in the page.
 429
 430If you want to create a new element for your view, set any combination of the following properties on the view: `tagName`, `id`, and `className`. A new element will be created for you by the framework and a reference to it will be available at the `el` property. If nothing is specified `tagName` defaults to `div`.
 431
 432In the example above, `tagName` is set to 'li', resulting in creation of an li element. The following example creates a ul element with id and class attributes:
 433
 434```javascript
 435var TodosView = Backbone.View.extend({
 436  tagName: 'ul', // required, but defaults to 'div' if not set
 437  className: 'container', // optional, you can assign multiple classes to this property like so: 'container homepage'
 438  id: 'todos', // optional
 439});
 440
 441var todosView = new TodosView();
 442console.log(todosView.el); // logs <ul id="todos" class="container"></ul>
 443```
 444
 445The above code creates the DOM element below but doesn't append it to the DOM.
 446
 447```html
 448<ul id="todos" class="container"></ul>
 449```
 450
 451If the element already exists in the page, you can set `el` as a CSS selector that matches the element.
 452
 453```javascript
 454el: '#footer'
 455```
 456
 457Alternatively, you can set `el` to an existing element when creating the view:
 458
 459```javascript
 460var todosView = new TodosView({el: $('#footer')});
 461```
 462
 463Note: When declaring a View, `options`, `el`, `tagName`, `id` and `className` may be defined as functions, if you want their values to be determined at runtime.
 464
 465**$el and $()**
 466
 467View logic often needs to invoke jQuery or Zepto functions on the `el` element and elements nested within it. Backbone makes it easy to do so by defining the `$el` property and `$()` function. The `view.$el` property is equivalent to `$(view.el)` and `view.$(selector)` is equivalent to `$(view.el).find(selector)`. In our TodosView example's render method, we see `this.$el` used to set the HTML of the element and `this.$()` used to find subelements of class 'edit'.
 468
 469**setElement**
 470
 471If you need to apply an existing Backbone view to a different DOM element `setElement` can be used for this purpose. Overriding this.el needs to both change the DOM reference and re-bind events to the new element (and unbind from the old). 
 472
 473`setElement` will create a cached `$el` reference for you, moving the delegated events for a view from the old element to the new one.
 474
 475```javascript
 476
 477// We create two DOM elements representing buttons
 478// which could easily be containers or something else
 479var button1 = $('<button></button>');
 480var button2 = $('<button></button>');
 481
 482// Define a new view
 483var View = Backbone.View.extend({
 484      events: {
 485        click: function(e) {
 486          console.log(view.el === e.target);
 487        }
 488      }
 489    });
 490
 491// Create a new instance of the view, applying it
 492// to button1
 493var view = new View({el: button1});
 494
 495// Apply the view to button2 using setElement
 496view.setElement(button2);
 497
 498button1.trigger('click'); 
 499button2.trigger('click'); // returns true
 500```
 501
 502The "el" property represents the markup portion of the view that will be rendered; to get the view to actually render to the page, you need to add it as a new element or append it to an existing element.
 503
 504```javascript
 505
 506// We can also provide raw markup to setElement
 507// as follows (just to demonstrate it can be done):
 508var view = new Backbone.View;
 509view.setElement('<p><a><b>test</b></a></p>');
 510view.$('a b').html(); // outputs "test"
 511```
 512
 513**Understanding `render()`**
 514
 515`render()` is an optional function that defines the logic for rendering a template. We'll use Underscore's micro-templating in these examples, but remember you can use other templating frameworks if you prefer. Our example will reference the following HTML markup:
 516
 517```html
 518<!doctype html>
 519<html lang="en">
 520<head>
 521  <meta charset="utf-8">
 522  <title></title>
 523  <meta name="description" content="">
 524</head>
 525<body>
 526  <div id="todo">
 527  </div>
 528  <script type="text/template" id="item-template">
 529    <div>
 530      <input id="todo_complete" type="checkbox" <%= completed ? 'checked="checked"' : '' %>>
 531      <%= title %>
 532    </div>
 533  </script>
 534  <script src="underscore-min.js"></script>
 535  <script src="backbone-min.js"></script>
 536  <script src="jquery-min.js"></script>
 537  <script src="example.js"></script>
 538</body>
 539</html>
 540```
 541
 542The `_.template` method in Underscore compiles JavaScript templates into functions which can be evaluated for rendering. In the TodoView, I'm passing the markup from the template with id `item-template` to `_.template()` to be compiled and stored in the todoTpl property when the view is created.
 543
 544The `render()` method uses this template by passing it the `toJSON()` encoding of the attributes of the model associated with the view. The template returns its markup after using the model's title and completed flag to evaluate the expressions containing them. I then set this markup as the HTML content of the `el` DOM element using the `$el` property.
 545
 546Presto! This populates the template, giving you a data-complete set of markup in just a few short lines of code.
 547
 548
 549
 550A common Backbone convention is to return `this` at the end of `render()`. This is useful for a number of reasons, including:
 551
 552* Making views easily reusable in other parent views.
 553* Creating a list of elements without rendering and painting each of them individually, only to be drawn once the entire list is populated.
 554
 555Let's try to implement the latter of these. The `render` method of a simple ListView which doesn't use an ItemView for each item could be written:
 556
 557```javascript
 558
 559var ListView = Backbone.View.extend({
 560  render: function(){
 561    this.$el.html(this.model.toJSON());
 562  }
 563});
 564```
 565
 566Simple enough. Let's now assume a decision is made to construct the items using an ItemView to provide enhanced behaviour to our list. The ItemView could be written:
 567
 568```javascript
 569
 570var ItemView = Backbone.View.extend({
 571  events: {},
 572  render: function(){
 573    this.$el.html(this.model.toJSON());
 574    return this;
 575  }
 576});
 577
 578```
 579
 580Note the usage of `return this;` at the end of `render`. This common pattern enables us to reuse the view as a sub-view. We can also use it to pre-render the view prior to rendering. Using this requires that we make a change to our ListView's `render` method as follows:
 581
 582```javascript
 583
 584var ListView = Backbone.View.extend({
 585  render: function(){
 586
 587    // Assume our model exposes the items we will
 588    // display in our list
 589    var items = this.model.get('items');
 590
 591    // Loop through each our items using the Underscore
 592    // _.each iterator
 593    _.each(items, function(item){
 594
 595      // Create a new instance of the ItemView, passing 
 596      // it a specific model item
 597      var itemView = new ItemView({ model: item });
 598      // The itemView's DOM element is appended after it
 599      // has been rendered. Here, the 'return this' is helpful
 600      // as the itemView renders its model. Later, we ask for 
 601      // its output ("el")
 602      this.$el.append( itemView.render().el );
 603    }, this);
 604  }
 605});
 606```
 607
 608**The `events` hash**
 609
 610
 611The Backbone `events` hash allows us to attach event listeners to either `el`-relative custom selectors, or directly to `el` if no selector is provided. An event takes the form of a key-value pair `'eventName selector': 'callbackFunction'` and a number of DOM event-types are supported, including `click`, `submit`, `mouseover`, `dblclick` and more.
 612
 613```javascript
 614
 615// A sample view
 616var TodoView = Backbone.View.extend({
 617  tagName:  'li',
 618
 619  // with an events hash containing DOM events
 620  // specific to an item:
 621  events: {
 622    'click .toggle': 'toggleCompleted',
 623    'dblclick label': 'edit',
 624    'click .destroy': 'clear',
 625    'blur .edit': 'close'
 626  },
 627```
 628
 629What isn't instantly obvious is that while Backbone uses jQuery's `.delegate()` underneath, it goes further by extending it so that `this` always refers to the current view object within callback functions. The only thing to really keep in mind is that any string callback supplied to the events attribute must have a corresponding function with the same name within the scope of your view. 
 630
 631The declarative, delegated jQuery events means that you don't have to worry about whether a particular element has been rendered to the DOM yet or not. Usually with jQuery you have to worry about "presence or absence in the DOM" all the time when binding events.
 632
 633In our TodoView example, the edit callback is invoked when the user double-clicks a label element within the `el` element, updateOnEnter is called for each keypress in an element with class 'edit', and close executes when an element with class 'edit' loses focus. Each of these callback functions can use `this` to refer to the TodoView object.
 634
 635Note that you can also bind methods yourself using `_.bind(this.viewEvent, this)`, which is effectively what the value in each event's key-value pair is doing. Below we use `_.bind` to re-render our view when a model changes.
 636
 637```javascript
 638
 639var TodoView = Backbone.View.extend({
 640  initialize: function() {
 641    this.model.bind('change', _.bind(this.render, this));
 642  }
 643});
 644```
 645
 646`_.bind` only works on one method at a time, but supports currying and as it returns the bound function means that you can use `_.bind` on an anonymous function.
 647
 648
 649## Collections
 650
 651Collections are sets of Models and are created by extending `Backbone.Collection`.
 652
 653Normally, when creating a collection you'll also want to define a property specifying the type of model that your collection will contain, along with any instance properties required.
 654
 655In the following example, we create a TodoCollection that will contain our Todo models:
 656
 657```javascript
 658var Todo = Backbone.Model.extend({
 659  defaults: {
 660    title: '',
 661    completed: false
 662  }
 663});
 664
 665var TodosCollection = Backbone.Collection.extend({
 666  model: Todo
 667});
 668
 669var myTodo = new Todo({title:'Read the whole book', id: 2});
 670
 671// pass array of models on collection instantiation
 672var todos = new TodosCollection([myTodo]);
 673console.log("Collection size: " + todos.length); // Collection size: 1
 674```
 675
 676#### Adding and Removing Models
 677
 678The preceding example populated the collection using an array of models when it was instantiated. After a collection has been created, models can be added and removed using the `add()` and `remove()` methods:
 679
 680```javascript
 681var Todo = Backbone.Model.extend({
 682  defaults: {
 683    title: '',
 684    completed: false
 685  }
 686});
 687
 688var TodosCollection = Backbone.Collection.extend({
 689  model: Todo,
 690});
 691
 692var a = new Todo({ title: 'Go to Jamaica.'}),
 693    b = new Todo({ title: 'Go to China.'}),
 694    c = new Todo({ title: 'Go to Disneyland.'});
 695
 696var todos = new TodosCollection([a,b]);
 697console.log("Collection size: " + todos.length);
 698// Logs: Collection size: 2
 699
 700todos.add(c);
 701console.log("Collection size: " + todos.length);
 702// Logs: Collection size: 3
 703
 704todos.remove([a,b]);
 705console.log("Collection size: " + todos.length);
 706// Logs: Collection size: 1
 707
 708todos.remove(c);
 709console.log("Collection size: " + todos.length);
 710// Logs: Collection size: 0
 711```
 712
 713Note that `add()` and `remove()` accept both individual models and lists of models.
 714
 715Also note that when using `add()` on a collection, passing `{merge: true}` causes duplicate models to have their attributes merged in to the existing models, instead of being ignored.
 716
 717```javascript
 718var items = new Backbone.Collection;
 719items.add([{ id : 1, name: "Dog" , age: 3}, { id : 2, name: "cat" , age: 2}]);
 720items.add([{ id : 1, name: "Bear" }], {merge: true });
 721items.add([{ id : 2, name: "lion" }]); // merge: false
 722 
 723console.log(JSON.stringify(items.toJSON()));
 724// [{"id":1,"name":"Bear","age":3},{"id":2,"name":"cat","age":2}]
 725```
 726
 727#### Retrieving Models
 728
 729There are a few different ways to retrieve a model from a collection. The most straight-forward is to use `Collection.get()` which accepts a single id as follows:
 730
 731```javascript
 732var myTodo = new Todo({title:'Read the whole book', id: 2});
 733
 734// pass array of models on collection instantiation
 735var todos = new TodosCollection([myTodo]);
 736
 737var todo2 = todos.get(2);
 738
 739// Models, as objects, are passed by reference
 740console.log(todo2 === myTodo); // true
 741```
 742
 743In client-server applications, collections contain models obtained from the server. Anytime you're exchanging data between the client and a server, you will need a way to uniquely identify models. In Backbone, this is done using the `id`, `cid`, and `idAttribute` properties.
 744
 745Each model in Backbone has an `id`, which is a unique identifier that is either an integer or string (e.g., a UUID). Models also have a `cid` (client id) which is automatically generated by Backbone when the model is created. Either identifier can be used to retrieve a model from a collection. 
 746
 747The main difference between them is that the `cid` is generated by Backbone; it is helpful when you don't have a true id - this may be the case if your model has yet to be saved to the server or you aren't saving it to a database.
 748
 749The `idAttribute` is the identifying attribute of the model returned from the server (i.e., the `id` in your database). This tells Backbone which data field from the server should be used to populate the `id` property (think of it as a mapper). By default, it assumes `id`, but this can be customized as needed. For instance, if your server sets a unique attribute on your model named "userId" then you would set `idAttribute` to "userId" in your model definition.
 750
 751The value of a model's idAttribute should be set by the server when the model is saved. After this point you shouldn't need to set it manually, unless further control is required. 
 752
 753Internally, `Backbone.Collection` contains an array of models enumerated by their `id` property, if the model instances happen to have one. When `collection.get(id)` is called, this array is checked for existence of the model instance with the corresponding `id`.
 754
 755```javascript
 756// extends the previous example
 757
 758var todoCid = todos.get(todo2.cid);
 759
 760// As mentioned in previous example, 
 761// models are passed by reference
 762console.log(todoCid === myTodo); // true
 763```
 764
 765#### Listening for events
 766
 767As collections represent a group of items, we can listen for `add` and `remove` events which occur when models are added to or removed from a collection. Here's an example:
 768
 769```javascript
 770var TodosCollection = new Backbone.Collection();
 771
 772TodosCollection.on("add", function(todo) {
 773  console.log("I should " + todo.get("title") + ". Have I done it before? "  + (todo.get("completed") ? 'Yeah!': 'No.' ));
 774});
 775
 776TodosCollection.add([
 777  { title: 'go to Jamaica', completed: false },
 778  { title: 'go to China', completed: false },
 779  { title: 'go to Disneyland', completed: true }
 780]);
 781
 782// The above logs:
 783// I should go to Jamaica. Have I done it before? No.
 784// I should go to China. Have I done it before? No.
 785// I should go to Disneyland. Have I done it before? Yeah!
 786```
 787
 788In addition, we're also able to bind to a `change` event to listen for changes to any of the models in the collection.
 789
 790```javascript
 791var TodosCollection = new Backbone.Collection();
 792
 793// log a message if a model in the collection changes
 794TodosCollection.on("change:title", function(model) {
 795    console.log("Changed my mind! I should " + model.get('title'));
 796});
 797
 798TodosCollection.add([
 799  { title: 'go to Jamaica.', completed: false, id: 3 },
 800]);
 801
 802var myTodo = TodosCollection.get(3);
 803
 804myTodo.set('title', 'go fishing');
 805// Logs: Changed my mind! I should go fishing
 806```
 807
 808jQuery-style event maps of the form `obj.on({click: action})` can also be used. These can be clearer than needing three separate calls to `.on` and should align better with the events hash used in Views:
 809
 810```javascript
 811
 812var Todo = Backbone.Model.extend({
 813  defaults: {
 814    title: '',
 815    completed: false
 816  }
 817});
 818
 819var myTodo = new Todo();
 820myTodo.set({title: 'Buy some cookies', completed: true});
 821
 822myTodo.on({
 823   'change:title' : titleChanged,
 824   'change:completed' : stateChanged
 825});
 826
 827function titleChanged(){
 828  console.log('The title was changed!');
 829}
 830
 831function stateChanged(){
 832  console.log('The state was changed!');
 833}
 834
 835myTodo.set({title: 'Get the groceries'});
 836// The title was changed! 
 837```
 838
 839Backbone events also support a [once()](http://backbonejs.org/#Events-once) method, which ensures that a callback only fires one time when a notification arrives. It is similar to Node's [once](http://nodejs.org/api/events.html#events_emitter_once_event_listener), or jQuery's [one](http://api.jquery.com/one/). This is particularly useful for when you want to say "the next time something happens, do this".
 840
 841```javascript
 842// Define an object with two counters
 843var TodoCounter = { counterA: 0, counterB: 0 };
 844// Mix in Backbone Events
 845_.extend(TodoCounter, Backbone.Events);
 846
 847// Increment counterA, triggering an event
 848var incrA = function(){ 
 849  TodoCounter.counterA += 1; 
 850  TodoCounter.trigger('event'); 
 851};
 852
 853// Increment counterB
 854var incrB = function(){ 
 855  TodoCounter.counterB += 1; 
 856};
 857
 858// Use once rather than having to explicitly unbind
 859// our event listener
 860TodoCounter.once('event', incrA);
 861TodoCounter.once('event', incrB);
 862
 863// Trigger the event once again
 864TodoCounter.trigger('event');
 865
 866// Check out output
 867console.log(TodoCounter.counterA === 1); // true
 868console.log(TodoCounter.counterB === 1); // true
 869```
 870
 871`counterA` and `counterB` should only have been incremented once.
 872
 873#### Resetting/Refreshing Collections
 874
 875Rather than adding or removing models individually, you might want to update an entire collection at once. `Collection.set()` takes an array of models and performs the necessary add, remove, and change operations required to update the collection.
 876
 877```javascript
 878var TodosCollection = new Backbone.Collection();
 879
 880TodosCollection.add([
 881    { id: 1, title: 'go to Jamaica.', completed: false },
 882    { id: 2, title: 'go to China.', completed: false },
 883    { id: 3, title: 'go to Disneyland.', completed: true }
 884]);
 885
 886// we can listen for add/change/remove events
 887TodosCollection.on("add", function(model) {
 888  console.log("Added " + model.get('title'));
 889});
 890
 891TodosCollection.on("remove", function(model) {
 892  console.log("Removed " + model.get('title'));
 893});
 894
 895TodosCollection.on("change:completed", function(model) {
 896  console.log("Completed " + model.get('title'));
 897});
 898
 899TodosCollection.set([
 900    { id: 1, title: 'go to Jamaica.', completed: true },
 901    { id: 2, title: 'go to China.', completed: false },
 902    { id: 4, title: 'go to Disney World.', completed: false }
 903]);
 904
 905// Above logs:
 906// Removed go to Disneyland.
 907// Completed go to Jamaica.
 908// Added go to Disney World.
 909```
 910
 911If you need to simply replace the entire content of the collection then `Collection.reset()` can be used:
 912
 913```javascript
 914var TodosCollection = new Backbone.Collection();
 915
 916// we can listen for reset events
 917TodosCollection.on("reset", function() {
 918  console.log("Collection reset.");
 919});
 920
 921TodosCollection.add([
 922  { title: 'go to Jamaica.', completed: false },
 923  { title: 'go to China.', completed: false },
 924  { title: 'go to Disneyland.', completed: true }
 925]);
 926
 927console.log('Collection size: ' + TodosCollection.length); // Collection size: 3
 928
 929TodosCollection.reset([
 930  { title: 'go to Cuba.', completed: false }
 931]);
 932// Above logs 'Collection reset.'
 933
 934console.log('Collection size: ' + TodosCollection.length); // Collection size: 1
 935```
 936
 937Another useful tip is to use `reset` with no arguments to clear out a collection completely. This is handy when dynamically loading a new page of results where you want to blank out the current page of results.
 938
 939```javascript
 940myCollection.reset();
 941```
 942
 943Note that using `Collection.reset()` doesn't fire any `add` or `remove` events. A `reset` event is fired instead as shown in the previous example. The reason you might want to use this is to perform super-optimized rendering in extreme cases where individual events are too expensive.
 944
 945Also note that listening to a [reset](http://backbonejs.org/#Collection-reset) event, the list of previous models is available in `options.previousModels`, for convenience.
 946
 947```javascript
 948var Todo = new Backbone.Model();
 949var Todos = new Backbone.Collection([Todo])
 950.on('reset', function(Todos, options) {
 951  console.log(options.previousModels);
 952  console.log([Todo]);
 953  console.log(options.previousModels[0] === Todo); // true
 954});
 955Todos.reset([]);
 956```
 957
 958An `update()` method is available for Collections (which is also available as an option to fetch) for "smart" updating of sets of models. This method attempts to perform smart updating of a collection using a specified list of models. When a model in this list isn't present in the collection, it is added. If it is, its attributes will be merged. Models which are present in the collection but not in the list are removed.
 959
 960```javascript
 961var theBeatles = new Collection(['john', 'paul', 'george', 'ringo']);
 962
 963theBeatles.update(['john', 'paul', 'george', 'pete']);
 964
 965// Fires a `remove` event for 'ringo', and an `add` event for 'pete'.
 966// Updates any of john, paul and georges's attributes that may have
 967// changed over the years.
 968```
 969
 970#### Underscore utility functions
 971
 972Backbone takes full advantage of its hard dependency on Underscore by making many of its utilities directly available on collections:
 973
 974**`forEach`: iterate over collections**
 975
 976```javascript
 977var Todos = new Backbone.Collection();
 978
 979Todos.add([
 980  { title: 'go to Belgium.', completed: false },
 981  { title: 'go to China.', completed: false },
 982  { title: 'go to Austria.', completed: true }
 983]);
 984
 985// iterate over models in the collection
 986Todos.forEach(function(model){
 987  console.log(model.get('title'));
 988});
 989// Above logs:
 990// go to Belgium.
 991// go to China.
 992// go to Austria.
 993```
 994
 995**`sortBy()`: sort a collection on a specific attribute**
 996
 997```javascript
 998// sort collection
 999var sortedByAlphabet = Todos.sortBy(function (todo) {
1000    return todo.get("title").toLowerCase();
1001});
1002
1003console.log("- Now sorted: ");
1004
1005sortedByAlphabet.forEach(function(model){
1006  console.log(model.get('title'));
1007});
1008// Above logs:
1009// go to Austria.
1010// go to Belgium.
1011// go to China.
1012```
1013
1014**`map()`: iterate through a collection, mapping each value through a transformation function**
1015
1016```javascript
1017var count = 1;
1018console.log(Todos.map(function(model){
1019  return count++ + ". " + model.get('title');
1020}));
1021// Above logs:
1022//1. go to Belgium.
1023//2. go to China.
1024//3. go to Austria.
1025```
1026
1027**`min()`/`max()`: retrieve item with the min or max value of an attribute**
1028
1029```javascript
1030Todos.max(function(model){
1031  return model.id;
1032}).id;
1033
1034Todos.min(function(model){
1035  return model.id;
1036}).id;
1037```
1038
1039**`pluck()`: extract a specific attribute**
1040
1041```javascript
1042var captions = Todos.pluck('caption');
1043// returns list of captions
1044```
1045
1046**`filter()`: filter a collection**
1047
1048*Filter by an array of model IDs*
1049
1050```javascript
1051var Todos = Backbone.Collection.extend({
1052  model: Todo,
1053  filterById: function(ids){
1054    return this.models.filter(
1055      function(c) { 
1056        return _.contains(ids, c.id); 
1057      })
1058  }
1059});
1060```
1061
1062**`indexOf()`: return the item at a particular index within a collection**
1063
1064```javascript
1065var People = new Backbone.Collection;
1066
1067People.comparator = function(a, b) {
1068  return a.get('name') < b.get('name') ? -1 : 1;
1069};
1070
1071var tom = new Backbone.Model({name: 'Tom'});
1072var rob = new Backbone.Model({name: 'Rob'});
1073var tim = new Backbone.Model({name: 'Tim'});
1074
1075People.add(tom);
1076People.add(rob);
1077People.add(tim);
1078
1079console.log(People.indexOf(rob) === 0); // true
1080console.log(People.indexOf(tim) === 1); // true
1081console.log(People.indexOf(tom) === 2); // true
1082```
1083
1084**`any()`: Confirm if any of the values in a collection pass an iterator truth test**
1085
1086```javascript
1087Todos.any(function(model){
1088  return model.id === 100;
1089});
1090
1091// or
1092Todos.some(function(model){
1093  return model.id === 100;
1094});
1095```
1096
1097**`size()`: return the size of a collection**
1098
1099```javascript
1100Todos.size();
1101
1102// equivalent to
1103Todos.length;
1104```
1105
1106**`isEmpty()`: determine whether a collection is empty**
1107
1108```javascript
1109var isEmpty = Todos.isEmpty();
1110```
1111
1112**`groupBy()`: group a collection into groups of like items**
1113
1114```javascript
1115var Todos = new Backbone.Collection();
1116
1117Todos.add([
1118  { title: 'go to Belgium.', completed: false },
1119  { title: 'go to China.', completed: false },
1120  { title: 'go to Austria.', completed: true }
1121]);
1122
1123// create groups of completed and incomplete models
1124var byCompleted = Todos.groupBy('completed');
1125var completed = new Backbone.Collection(byCompleted[true]);
1126console.log(completed.pluck('title'));
1127// logs: ["go to Austria."]
1128```
1129
1130In addition, several of the Underscore operations on objects are available as methods on Models.
1131
1132**`pick()`: extract a set of attributes from a model**
1133
1134```javascript
1135var Todo = Backbone.Model.extend({
1136  defaults: {
1137    title: '',
1138    completed: false
1139  }
1140});
1141
1142var todo = new Todo({title: 'go to Austria.'});
1143console.log(todo.pick('title'));
1144// logs {title: "go to Austria"}
1145```
1146
1147**`omit()`: extract all attributes from a model except those listed**
1148
1149```javascript
1150var todo = new Todo({title: 'go to Austria.'});
1151console.log(todo.omit('title'));
1152// logs {completed: false}
1153```
1154
1155**`keys()` and `values()`: get lists of attribute names and values**
1156
1157```javascript
1158var todo = new Todo({title: 'go to Austria.'});
1159console.log(todo.keys());
1160// logs: ["title", "completed"]
1161
1162console.log(todo.values());
1163//logs: ["go to Austria.", false]
1164```
1165
1166**`pairs()`: get list of attributes as [key, value] pairs**
1167
1168```javascript
1169var todo = new Todo({title: 'go to Austria.'});
1170var pairs = todo.pairs();
1171
1172console.log(pairs[0]);
1173// logs: ["title", "go to Austria."]
1174console.log(pairs[1]);
1175// logs: ["completed", false]
1176```
1177
1178**`invert()`: create object in which the values are keys and the attributes are values**
1179
1180```javascript
1181var todo = new Todo({title: 'go to Austria.'});
1182console.log(todo.invert());
1183
1184// logs: {go to Austria.: "title", false: "completed"}
1185```
1186
1187The complete list of what Underscore can do can be found in its official [docs](http://documentcloud.github.com/underscore/).
1188
1189#### Chainable API
1190
1191Speaking of utility methods, another bit of sugar in Backbone is its support for Underscore’s `chain()` method. Chaining is a common idiom in object-oriented languages; a chain is a sequence of method calls on the same object that are performed in a single statement. While Backbone makes Underscore's array manipulation operations available as methods of Collection objects, they cannot be directly chained since they return arrays rather than the original Collection.
1192
1193Fortunately, the inclusion of Underscore's `chain()` method enables you to chain calls to these methods on Collections.
1194
1195The `chain()` method returns an object that has all of the Underscore array operations attached as methods which return that object. The chain ends with a call to the `value()` method which simply returns the resulting array value. In case you haven’t seen it before, the chainable API looks like this:
1196
1197```javascript
1198var collection = new Backbone.Collection([
1199  { name: 'Tim', age: 5 },
1200  { name: 'Ida', age: 26 },
1201  { name: 'Rob', age: 55 }
1202]);
1203
1204var filteredNames = collection.chain() // start chain, returns wrapper around collection's models
1205  .filter(function(item) { return item.get('age') > 10; }) // returns wrapped array excluding Tim
1206  .map(function(item) { return item.get('name'); }) // returns wrapped array containing remaining names
1207  .value(); // terminates the chain and returns the resulting array
1208
1209console.log(filteredNames); // logs: ['Ida', 'Rob']
1210```
1211
1212Some of the Backbone-specific methods do return `this`, which means they can be chained as well:
1213
1214```javascript
1215var collection = new Backbone.Collection();
1216
1217collection
1218    .add({ name: 'John', age: 23 })
1219    .add({ name: 'Harry', age: 33 })
1220    .add({ name: 'Steve', age: 41 });
1221
1222var names = collection.pluck('name');
1223
1224console.log(names); // logs: ['John', 'Harry', 'Steve']
1225```
1226
1227## RESTful Persistence
1228
1229Thus far, all of our example data has been created in the browser. For most single page applications, the models are derived from a data set residing on a server. This is an area in which Backbone dramatically simplifies the code you need to write to perform RESTful synchronization with a server through a simple API on its models and collections.
1230
1231**Fetching models from the server**
1232
1233`Collections.fetch()` retrieves a set of models from the server in the form of a JSON array by sending an HTTP GET request to the URL specified by the collection's `url` property (which may be a function). When this data is received, a `set()` will be executed to update the collection.
1234
1235```javascript
1236var Todo = Backbone.Model.extend({
1237  defaults: {
1238    title: '',
1239    completed: false
1240  }
1241});
1242
1243var TodosCollection = Backbone.Collection.extend({
1244  model: Todo,
1245  url: '/todos'
1246});
1247
1248var todos = new TodosCollection();
1249todos.fetch(); // sends HTTP GET to /todos
1250```
1251
1252**Saving models to the server**
1253
1254While Backbone can retrieve an entire collection of models from the server at once, updates to models are performed individually using the model's `save()` method. When `save()` is called on a model that was fetched from the server, it constructs a URL by appending the model's id to the collection's URL and sends an HTTP PUT to the server. If the model is a new instance that was created in the browser (i.e., it doesn't have an id) then an HTTP POST is sent to the collection's URL. `Collections.create()` can be used to create a new model, add it to the collection, and send it to the server in a single method call.
1255
1256```javascript
1257var Todo = Backbone.Model.extend({
1258  defaults: {
1259    title: '',
1260    completed: false
1261  }
1262});
1263
1264var TodosCollection = Backbone.Collection.extend({
1265  model: Todo,
1266  url: '/todos'
1267});
1268
1269var todos = new TodosCollection();
1270todos.fetch();
1271
1272var todo2 = todos.get(2);
1273todo2.set('title', 'go fishing');
1274todo2.save(); // sends HTTP PUT to /todos/2
1275
1276todos.create({title: 'Try out code samples'}); // sends HTTP POST to /todos and adds to collection
1277```
1278
1279As mentioned earlier, a model's `validate()` method is called automatically by `save()` and will trigger an `invalid` event on the model if validation fails.
1280
1281**Deleting models from the server**
1282
1283A model can be removed from the containing collection and the server by calling its `destroy()` method. Unlike `Collection.remove()` which only removes a model from a collection, `Model.destroy()` will also send an HTTP DELETE to the collection's URL.
1284
1285```javascript
1286var Todo = Backbone.Model.extend({
1287  defaults: {
1288    title: '',
1289    completed: false
1290  }
1291});
1292
1293var TodosCollection = Backbone.Collection.extend({
1294  model: Todo,
1295  url: '/todos'
1296});
1297
1298var todos = new TodosCollection();
1299todos.fetch();
1300
1301var todo2 = todos.get(2);
1302todo2.destroy(); // sends HTTP DELETE to /todos/2 and removes from collection
1303```
1304
1305Calling `destroy` on a Model will return `false` if the model `isNew`:
1306
1307```javascript
1308var Todo = new Backbone.Model();
1309console.log(Todo.destroy());
1310// false
1311```
1312
1313**Options**
1314
1315Each RESTful API method accepts a variety of options. Most importantly, all methods accept success and error callbacks which can be used to customize the handling of server responses. 
1316
1317Specifying the `{patch: true}` option to `Model.save()` will cause it to use HTTP PATCH to send only the changed attributes (i.e partial updates) to the server instead of the entire model i.e `model.save(attrs, {patch: true})`:
1318
1319```javascript
1320// Save partial using PATCH
1321model.clear().set({id: 1, a: 1, b: 2, c: 3, d: 4});
1322model.save();
1323model.save({b: 2, d: 4}, {patch: true});
1324console.log(this.syncArgs.method);
1325// 'patch'
1326```
1327
1328Similarly, passing the `{reset: true}` option to `Collection.fetch()` will result in the collection being updated using `reset()` rather than `set()`.
1329
1330See the Backbone.js documentation for full descriptions of the supported options.
1331
1332## Events
1333
1334Events are a basic inversion of control. Instead of having one function call another by name, the second function is registered as a handler to be called when a specific event occurs.
1335
1336The part of your application that has to know how to call the other part of your app has been inverted. This is the core thing that makes it possible for your business logic to not have to know about how your user interface works and is the most powerful thing about the Backbone Events system.
1337
1338Mastering events is one of the quickest ways to become more productive with Backbone, so let's take a closer look at Backbone's event model.
1339
1340`Backbone.Events` is mixed into the other Backbone "classes", including:
1341
1342* Backbone
1343* Backbone.Model
1344* Backbone.Collection
1345* Backbone.Router
1346* Backbone.History
1347* Backbone.View
1348
1349Note that `Backbone.Events` is mixed into the `Backbone` object. Since `Backbone` is globally visible, it can be used as a simple event bus:
1350
1351```javascript
1352Backbone.on('event', function() {console.log('Handled Backbone event');});
1353Backbone.trigger('event'); // logs: Handled Backbone event
1354```
1355
1356#### on(), off(), and trigger()
1357
1358`Backbone.Events` can give any object the ability to bind and trigger custom events. We can mix this module into any object easily and there isn't a requirement for events to be declared before being bound to a callback handler.
1359
1360Example:
1361
1362```javascript
1363var ourObject = {};
1364
1365// Mixin
1366_.extend(ourObject, Backbone.Events);
1367
1368// Add a custom event
1369ourObject.on('dance', function(msg){
1370  console.log('We triggered ' + msg);
1371});
1372
1373// Trigger the custom event
1374ourObject.trigger('dance', 'our event');
1375```
1376
1377If you're familiar with jQuery custom events or the concept of Publish/Subscribe, `Backbone.Events` provides a system that is very similar with `on` being analogous to `subscribe` and `trigger` being similar to `publish`.
1378
1379`on` binds a callback function to an object, as we've done with `dance` in the above example. The callback is invoked whenever the event is triggered.
1380
1381The official Backbone.js documentation recommends namespacing event names using colons if you end up using quite a few of these on your page. e.g.:
1382
1383```javascript
1384var ourObject = {};
1385
1386// Mixin
1387_.extend(ourObject, Backbone.Events);
1388
1389function dancing (msg) { console.log("We started " + msg); }
1390
1391// Add namespaced custom events
1392ourObject.on("dance:tap", dancing);
1393ourObject.on("dance:break", dancing);
1394
1395// Trigger the custom events
1396ourObject.trigger("dance:tap", "tap dancing. Yeah!");
1397ourObject.trigger("dance:break", "break dancing. Yeah!");
1398
1399// This one triggers nothing as no listener listens for it
1400ourObject.trigger("dance", "break dancing. Yeah!");
1401```
1402
1403A special `all` event is made available in case you would like notifications for every event that occurs on the object (e.g., if you would like to screen events in a single location). The `all` event can be used as follows:
1404
1405
1406```javascript
1407var ourObject = {};
1408
1409// Mixin
1410_.extend(ourObject, Backbone.Events);
1411
1412function dancing (msg) { console.log("We started " + msg); }
1413
1414ourObject.on("all", function(eventName){
1415  console.log("The name of the event passed was " + eventName);
1416});
1417
1418// This time each event will be caught with a catch 'all' event listener
1419ourObject.trigger("dance:tap", "tap dancing. Yeah!");
1420ourObject.trigger("dance:break", "break dancing. Yeah!");
1421ourObject.trigger("dance", "break dancing. Yeah!");
1422```
1423
1424`off` removes callback functions that were previously bound to an object. Going back to our Publish/Subscribe comparison, think of it as an `unsubscribe` for custom events.
1425
1426To remove the `dance` event we previously bound to `ourObject`, we would simply do:
1427
1428```javascript
1429var ourObject = {};
1430
1431// Mixin
1432_.extend(ourObject, Backbone.Events);
1433
1434function dancing (msg) { console.log("We " + msg); }
1435
1436// Add namespaced custom events
1437ourObject.on("dance:tap", dancing);
1438ourObject.on("dance:break", dancing);
1439
1440// Trigger the custom events. Each will be caught and acted upon.
1441ourObject.trigger("dance:tap", "started tap dancing. Yeah!");
1442ourObject.trigger("dance:break", "started break dancing. Yeah!");
1443
1444// Removes event bound to the object
1445ourObject.off("dance:tap");
1446
1447// Trigger the custom events again, but one is logged.
1448ourObject.trigger("dance:tap", "stopped tap dancing."); // won't be logged as it's not listened for
1449ourObject.trigger("dance:break", "break dancing. Yeah!");
1450```
1451
1452To remove all callbacks for the event we pass an event name (e.g., `move`) to the `off()` method on the object the event is bound to. If we wish to remove a specific callback, we can pass that callback as the second parameter:
1453
1454```javascript
1455var ourObject = {};
1456
1457// Mixin
1458_.extend(ourObject, Backbone.Events);
1459
1460function dancing (msg) { console.log("We are dancing. " + msg); }
1461function jumping (msg) { console.log("We are jumping. " + msg); }
1462
1463// Add two listeners to the same event
1464ourObject.on("move", dancing);
1465ourObject.on("move", jumping);
1466
1467// Trigger the events. Both listeners are called.
1468ourObject.trigger("move", "Yeah!");
1469
1470// Removes specified listener
1471ourObject.off("move", dancing);
1472
1473// Trigger the events again. One listener left.
1474ourObject.trigger("move", "Yeah, jump, jump!");
1475```
1476
1477Finally, as we have seen in our previous examples, `trigger` triggers a callback for a specified event (or a space-separated list of events). e.g.:
1478
1479```javascript
1480var ourObject = {};
1481
1482// Mixin
1483_.extend(ourObject, Backbone.Events);
1484
1485function doAction (msg) { console.log("We are " + msg); }
1486
1487// Add event listeners
1488ourObject.on("dance", doAction);
1489ourObject.on("jump", doAction);
1490ourObject.on("skip", doAction);
1491
1492// Single event
1493ourObject.trigger("dance", 'just dancing.');
1494
1495// Multiple events
1496ourObject.trigger("dance jump skip", 'very tired from so much action.');
1497```
1498
1499`trigger` can pass multiple arguments to the callback function:
1500
1501```javascript
1502var ourObject = {};
1503
1504// Mixin
1505_.extend(ourObject, Backbone.Events);
1506
1507function doAction (action, duration) {
1508  console.log("We are " + action + ' for ' + duration ); 
1509}
1510
1511// Add event listeners
1512ourObject.on("dance", doAction);
1513ourObject.on("jump", doAction);
1514ourObject.on("skip", doAction);
1515
1516// Passing multiple arguments to single event
1517ourObject.trigger("dance", 'dancing', "5 minutes");
1518
1519// Passing multiple arguments to multiple events
1520ourObject.trigger("dance jump skip", 'on fire', "15 minutes");
1521```
1522
1523#### listenTo() and stopListening()
1524
1525While `on()` and `off()` add callbacks directly to an observed object, `listenTo()` tells an object to listen for events on another object, allowing the listener to keep track of the events for which it is listening. `stopListening()` can subsequently be called on the listener to tell it to stop listening for events:
1526
1527```javascript
1528var a = _.extend({}, Backbone.Events);
1529var b = _.extend({}, Backbone.Events);
1530var c = _.extend({}, Backbone.Events);
1531
1532// add listeners to A for events on B and C
1533a.listenTo(b, 'anything', function(event){ console.log("anything happened"); });
1534a.listenTo(c, 'everything', function(event){ console.log("everything happened"); });
1535
1536// trigger an event
1537b.trigger('anything'); // logs: anything happened
1538
1539// stop listening
1540a.stopListening();
1541
1542// A does not receive these events
1543b.trigger('anything');
1544c.trigger('everything');
1545```
1546
1547`stopListening()` can also be used to selectively stop listening based on the event, model, or callback handler.
1548
1549If you use `on` and `off` and remove views and their corresponding models at the same time, there are generally no problems. But a problem arises when you remove a view that had registered to be notified about events on a model, but you don't remove the model or call `off` to remove the view's event handler. Since the model has a reference to the view's callback function, the JavaScript garbage collector cannot remove the view from memory. This is called a "ghost view" and is a form of memory leak which is common since the models generally tend to outlive the corresponding views during an application's lifecycle. For details on the topic and a solution, check this [excellent article](http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/) by Derick Bailey. 
1550
1551Practically, every `on` called on an object also requires an `off` to be called in order for the garbage collector to do its job. `listenTo()` changes that, allowing Views to bind to Model notifications and unbind from all of them with just one call - `stopListening()`.
1552
1553The default implementation of `View.remove()` makes a call to `stopListening()`, ensuring that any listeners bound using `listenTo()` are unbound before the view is destroyed.
1554
1555```javascript
1556var view = new Backbone.View();
1557var b = _.extend({}, Backbone.Events);
1558
1559view.listenTo(b, 'all', function(){ console.log(true); });
1560b.trigger('anything');  // logs: true
1561
1562view.listenTo(b, 'all', function(){ console.log(false); });
1563view.remove(); // stopListening() implicitly called
1564b.trigger('anything');  // does not log anything
1565```
1566
1567#### Events and Views
1568
1569Within a View, there are two types of events you can listen for: DOM events and events triggered using the Event API. It is important to understand the differences in how views bind to these events and the context in which their callbacks are invoked.
1570
1571DOM events can be bound to using the View's `events` property or using `jQuery.on()`. Within callbacks bound using the `events` property, `this` refers to the View object; whereas any callbacks bound directly using jQuery will have `this` set to the handling DOM element by jQuery. All DOM event callbacks are passed an `event` object by jQuery. See `delegateEvents()` in the Backbone documentation for additional details.
1572
1573Event API events are bound as described in this section. If the event is bound using `on()` on the observed object, a context parameter can be passed as the third argument. If the event is bound using `listenTo()` then within the callback `this` refers to the listener. The arguments passed to Event API callbacks depends on the type of event. See the Catalog of Events in the Backbone documentation for details.
1574
1575The following example illustrates these differences:
1576
1577```html
1578<div id="todo">
1579    <input type='checkbox' />
1580</div>
1581```
1582
1583```javascript
1584var View = Backbone.View.extend({
1585
1586    el: '#todo',
1587
1588    // bind to DOM event using events property
1589    events: {
1590        'click [type="checkbox"]': 'clicked',
1591    },
1592
1593    initialize: function () {
1594        // bind to DOM event using jQuery
1595        this.$el.click(this.jqueryClicked);
1596
1597        // bind to API event
1598        this.on('apiEvent', this.callback);
1599    },
1600
1601    // 'this' is view
1602    clicked: function(event) {
1603        console.log("events handler for " + this.el.outerHTML);
1604        this.trigger('apiEvent', event.type);
1605    },
1606
1607    // 'this' is handling DOM element
1608    jqueryClicked: function(event) {
1609        console.log("jQuery handler for " + this.outerHTML);
1610    },
1611
1612    callback: function(eventType) {
1613        console.log("event type was " + eventType);
1614    }
1615
1616});
1617
1618var view = new View();
1619```
1620
1621## Routers
1622
1623In Backbone, routers provide a way for you to connect URLs (either hash fragments, or
1624real) to parts of your application. Any piece of your application that you want
1625to be bookmarkable, shareable, and back-button-able, needs a URL.
1626
1627Some examples of routes using the hash mark may be seen below:
1628
1629```javascript
1630http://example.com/#about
1631http://example.com/#search/seasonal-horns/page2
1632```
1633
1634An application will usually have at least one route mapping a URL route to a function that determines what happens when a user reaches that route. This relationship is defined as follows:
1635
1636```javascript
1637'route' : 'mappedFunction'
1638```
1639
1640Let's define our first router by extending `Backbone.Router`. For the purposes of this guide, we're going to continue pretending we're creating a complex todo application (something like a personal organizer/planner) that requires a complex TodoRouter.
1641
1642Note the inline comments in the code example below as they continue our lesson on routers.
1643
1644```javascript
1645var TodoRouter = Backbone.Router.extend({
1646    /* define the route and function maps for this router */
1647    routes: {
1648        "about" : "showAbout",
1649        /* Sample usage: http://example.com/#about */
1650
1651        "todo/:id" : "getTodo",
1652        /* This is an example of using a ":param" variable which allows us to match
1653        any of the components between two URL slashes */
1654        /* Sample usage: http://example.com/#todo/5 */
1655
1656        "search/:query" : "searchTodos",
1657        /* We can also define multiple routes that are bound to the same map function,
1658        in this case searchTodos(). Note below how we're optionally passing in a
1659        reference to a page number if one is supplied */
1660        /* Sample usage: http://example.com/#search/job */
1661
1662        "search/:query/p:page" : "searchTodos",
1663        /* As we can see, URLs may contain as many ":param"s as we wish */
1664        /* Sample usage: http://example.com/#search/job/p1 */
1665
1666        "todos/:id/download/*documentPath" : "downloadDocument",
1667        /* This is an example of using a *splat. Splats are able to match any number of
1668        URL components and can be combined with ":param"s*/
1669        /* Sample usage: http://example.com/#todos/5/download/files/Meeting_schedule.doc */
1670
1671        /* If you wish to use splats for anything beyond default routing, it's probably a good
1672        idea to leave them at the end of a URL otherwise you may need to apply regular
1673        expression parsing on your fragment */
1674
1675        "*other"    : "defaultRoute"
1676        /* This is a default route that also uses a *splat. Consider the
1677        default route a wildcard for URLs that are either not matched or where
1678        the user has incorrectly typed in a route path manually */
1679        /* Sample usage: http://example.com/# <anything> */,
1680
1681        "optional(/:item)": "optionalItem",
1682        "named/optional/(y:z)": "namedOptionalItem"
1683        /* Router URLs also support optional parts via parentheses, without having
1684           to use a regex.  */
1685    },
1686
1687    showAbout: function(){
1688    },
1689
1690    getTodo: function(id){
1691        /*
1692        Note that the id matched in the above route will be passed to this function
1693        */
1694        console.log("You are trying to reach todo " + id);
1695    },
1696
1697    searchTodos: function(query, page){
1698        var page_number = page || 1;
1699        console.log("Page number: " + page_number + " of the results for todos containing the word: " + query);
1700    },
1701
1702    downloadDocument: function(id, path){
1703    },
1704
1705    defaultRoute: function(other){
1706        console.log('Invalid. You attempted to reach:' + other);
1707    }
1708});
1709
1710/* Now that we have a router setup, we need to instantiate it */
1711
1712var myTodoRouter = new TodoRouter();
1713```
1714
1715
1716Backbone offers an opt-in for HTML5 pushState support via `window.history.pushState`. This permits you to define routes such as http://backbonejs.org/just/an/example. This will be supported with automatic degradation when a user's browser doesn't support pushState. Note that it is vastly preferred if you're capable of also supporting pushState on the server side, although it is a little more difficult to implement.
1717
1718**Is there a limit to the number of routers I should be using?**
1719
1720Andrew de Andrade has pointed out that DocumentCloud, the creators of Backbone, usually only use a single router in most of their applications. You're very likely to not require more than one or two routers in your own projects; the majority of your application routing can be kept organized in a single router without it getting unwieldy.
1721
1722#### Backbone.history
1723
1724Next, we need to initialize `Backbone.history` as it handles `hashchange` events in our application. This will automatically handle routes that have been defined and trigger callbacks when they've been accessed.
1725
1726The `Backbone.history.start()` method will simply tell Backbone that it's okay to begin monitoring all `hashchange` events as follows:
1727
1728```javascript
1729var TodoRouter = Backbone.Router.extend({
1730  /* define the route and function maps for this router */
1731  routes: {
1732    "about" : "showAbout",
1733    "search/:query" : "searchTodos",
1734    "search/:query/p:page" : "searchTodos"
1735  },
1736
1737  showAbout: function(){},
1738
1739  searchTodos: function(query, page){
1740    var page_number = page || 1;
1741    console.log("Page number: " + page_number + " of the results for todos containing the word: " + query);
1742  }
1743});
1744
1745var myTodoRouter = new TodoRouter();
1746
1747Backbone.history.start();
1748
1749// Go to and check console:
1750// http://localhost/#search/job/p3   logs: Page number: 3 of the results for todos containing the word: job
1751// http://localhost/#search/job      logs: Page number: 1 of the results for todos containing the word: job 
1752// etc.
1753```
1754
1755Note: To run the last example, you'll need to create a local development environment and test project, which we will cover later on in the book.
1756
1757If you would like to update the URL to reflect the application state at a particular point, you can use the router's `.navigate()` method. By default, it simply updates your URL fragment without triggering the `hashchange` event:
1758
1759```javascript
1760// Let's imagine we would like a specific fragment (edit) once a user opens a single todo
1761var TodoRouter = Backbone.Router.extend({
1762  routes: {
1763    "todo/:id": "viewTodo",
1764    "todo/:id/edit": "editTodo"
1765    // ... other routes
1766  },
1767
1768  viewTodo: function(id){
1769    console.log("View todo requested.");
1770    this.navigate("todo/" + id + '/edit'); // updates the fragment for us, but doesn't trigger the route
1771  },
1772
1773  editTodo: function(id) {
1774    console.log("Edit todo opened.");
1775  }
1776});
1777
1778var myTodoRouter = new TodoRouter();
1779
1780Backbone.history.start();
1781
1782// Go to: http://localhost/#todo/4
1783//
1784// URL is updated to: http://localhost/#todo/4/edit
1785// but editTodo() function is not invoked even though location we end up is mapped to it.
1786//
1787// logs: View todo requested.
1788```
1789
1790It is also possible for `Router.navigate()` to trigger the route along with updating the URL fragment by passing the `trigger:true` option.
1791
1792Note: This usage is discouraged. The recommended usage is the one described above which creates a bookmarkable URL when your application transitions to a specific state.
1793
1794```javascript
1795var TodoRouter = Backbone.Router.extend({
1796  routes: {
1797    "todo/:id": "viewTodo",
1798    "todo/:id/edit": "editTodo"
1799    // ... other routes
1800  },
1801
1802  viewTodo: function(id){
1803    console.log("View todo requested.");
1804    this.navigate("todo/" + id + '/edit', {trigger: true}); // updates the fragment and triggers the route as well
1805  },
1806
1807  editTodo: function(id) {
1808    console.log("Edit todo opened.");
1809  }
1810});
1811
1812var myTodoRouter = new TodoRouter();
1813
1814Backbone.history.start();
1815
1816// Go to: http://localhost/#todo/4
1817//
1818// URL is updated to: http://localhost/#todo/4/edit
1819// and this time editTodo() function is invoked.
1820//
1821// logs:
1822// View todo requested.
1823// Edit todo opened.
1824```
1825
1826A "route" event is also triggered on the router in addition to being fired on Backbone.history.
1827
1828```javascript
1829Backbone.history.on('route', onRoute);
1830
1831// Trigger 'route' event on router instance."
1832router.on('route', function(name, args) {
1833  console.log(name === 'routeEvent'); 
1834});
1835
1836location.replace('http://example.com#route-event/x');
1837Backbone.history.checkUrl();
1838```
1839
1840## Backbone’s Sync API
1841
1842We previously discussed how Backbone supports RESTful persistence via its `fetch()` and `create()` methods on Collections and `save()`, and `delete()` methods on Models. Now we are going to take a closer look at Backbone's sync method which underlies these operations.
1843
1844The Backbone.sync method is an integral part of Backbone.js. It assumes a jQuery-like `$.ajax()` method, so HTTP parameters are organized based on jQuery’s API. Since some legacy servers may not support JSON-formatted requests and HTTP PUT and DELETE operations, Backbone can be configured to emulate these capabilities using the two configuration variables shown below with their default values:
1845
1846```javascript
1847Backbone.emulateHTTP = false; // set to true if server cannot handle HTTP PUT or HTTP DELETE
1848Backbone.emulateJSON = false; // set to true if server cannot handle application/json requests
1849```
1850
1851The inline Backbone.emulateHTTP option should be set to true if extended HTTP methods are not supported by the server. The Backbone.emulateJSON option should be set to true if the server does not understand the MIME type for JSON.
1852
1853```javascript
1854// Create a new library collection
1855var Library = Backbone.Collection.extend({
1856    url : function() { return '/library'; }
1857});
1858
1859// Define attributes for our model
1860var attrs = {
1861    title  : "The Tempest",
1862    author : "Bill Shakespeare",
1863    length : 123
1864};
1865  
1866// Create a new Library instance
1867var library = new Library;
1868
1869// Create a new instance of a model within our collection
1870library.create(attrs, {wait: false});
1871  
1872// Update with just emulateHTTP
1873library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}, {
1874  emulateHTTP: true
1875});
1876    
1877// Check the ajaxSettings being used for our request
1878console.log(this.ajaxSettings.url === '/library/2-the-tempest'); // true
1879console.log(this.ajaxSettings.type === 'POST'); // true
1880console.log(this.ajaxSettings.contentType === 'application/json'); // true
1881
1882// Parse the data for the request to confirm it is as expected
1883var data = JSON.parse(this.ajaxSettings.data);
1884console.log(data.id === '2-the-tempest');  // true
1885console.log(data.author === 'Tim Shakespeare'); // true
1886console.log(data.length === 123); // true
1887```
1888
1889Similarly, we could just update using `emulateJSON`:
1890
1891```javascript
1892library.first().save({id: '2-the-tempest', author: 'Tim Shakespeare'}, {
1893  emulateJSON: true
1894});
1895
1896console.log(this.ajaxSettings.url === '/library/2-the-tempest'); // true
1897console.log(this.ajaxSettings.type === 'PUT'); // true
1898console.log(this.ajaxSettings.contentType ==='application/x-www-form-urlencoded'); // true
1899
1900var data = JSON.parse(this.ajaxSettings.data.model);
1901console.log(data.id === '2-the-tempest');
1902console.log(data.author ==='Tim Shakespeare');
1903console.log(data.length === 123);
1904```
1905
1906`Backbone.sync` is called every time Backbone tries to read, save, or delete models. It uses jQuery or Zepto's `$.ajax()` implementations to make these RESTful requests, however this can be overridden as per your needs.
1907
1908**Overriding Backbone.sync**
1909
1910The `sync` function may be overridden globally as Backbone.sync, or at a finer-grained level, by adding a sync function to a Backbone collection or to an individual model.
1911
1912Since all persistence is handled by the Backbone.sync function, an alternative persistence layer can be used by simply overriding Backbone.sync with a function that has the same signature:
1913
1914```javascript
1915Backbone.sync = function(method, model, options) {
1916};
1917```
1918
1919The methodMap below is used by the standard sync implementation to map the method parameter to an HTTP operation and illustrates the type of action required by each method argument:
1920
1921```javascript
1922var methodMap = {
1923  'create': 'POST',
1924  'update': 'PUT',
1925  'patch':  'PATCH',
1926  'delete': 'DELETE',
1927  'read':   'GET'
1928};
1929```
1930
1931If we wanted to replace the standard `sync` implementation with one that simply logged the calls to sync, we could do this:
1932
1933```javascript
1934var id_counter = 1;
1935Backbone.sync = function(method, model) {
1936  console.log("I've been passed " + method + " with " + JSON.stringify(model));
1937  if(method === 'create'){ model.set('id', id_counter++); }
1938};
1939```
1940
1941Note that we assign a unique id to any created models.
1942
1943The Backbone.sync method is intended to be overridden to support other persistence backends. The built-in method is tailored to a certain breed of RESTful JSON APIs - Backbone was originally extracted from a Ruby on Rails application, which uses HTTP methods like PUT in the same way.
1944
1945The sync method is called with three parameters:
1946
1947* method: One of create, update, patch, delete, or read
1948* model: The Backbone model object
1949* options: May include success and error methods
1950
1951Implementing a new sync method can use the following pattern:
1952
1953```javascript
1954Backbone.sync = function(method, model, options) {
1955
1956  function success(result) {
1957    // Handle successful results from MyAPI
1958    if (options.success) {
1959      options.success(result);
1960    }
1961  }
1962
1963  function error(result) {
1964    // Handle error results from MyAPI
1965    if (options.error) {
1966      options.error(result);
1967    }
1968  }
1969
1970  options || (options = {});
1971
1972  switch (method) {
1973    case 'create':
1974      return MyAPI.create(model, success, error);
1975
1976    case 'update':
1977      return MyAPI.update(model, success, error);
1978
1979    case 'patch':
1980      return MyAPI.patch(model, success, error);
1981
1982    case 'delete':
1983      return MyAPI.destroy(model, success, error);
1984
1985    case 'read':
1986      if (model.attributes[model.idAttribute]) {
1987        return MyAPI.find(model, success, error);
1988      } else {
1989        return MyAPI.findAll(model, success, error);
1990      }
1991  }
1992};
1993```
1994
1995This pattern delegates API calls to a new object (MyAPI), which could be a Backbone-style class that supports events. This can be safely tested separately, and potentially used with libraries other than Backbone.
1996
1997There are quite a few sync implementations out there. The following examples are all available on GitHub:
1998
1999* Backbone localStorage: persists to the browser's local storage
2000* Backbone offline: supports working offline
2001* Backbone Redis: uses Redis key-value store
2002* backbone-parse: integrates Backbone with Parse.com
2003* backbone-websql: stores data to WebSQL
2004* Backbone Caching Sync: uses local storage as cache for other sync implementations
2005
2006
2007## Dependencies
2008
2009The official Backbone.js [documentation](http://backbonejs.org/) states:
2010
2011>Backbone's only hard dependency is either Underscore.js ( >= 1.4.3) or Lo-Dash. For RESTful persistence, history support via Backbone.Router and DOM manipulation with Backbone.View, include json2.js, and either jQuery ( >= 1.7.0) or Zepto.
2012
2013What this translates to is that if you require working with anything beyond models, you will need to include a DOM manipulation library such as jQuery or Zepto. Underscore is primarily used for its utility methods (which Backbone relies upon heavily) and json2.js for legacy browser JSON support if Backbone.sync is used.
2014
2015## Summary
2016
2017In this chapter we have introduced you to the components you will be using to build applications with Backbone: Models, Views, Collections, and Routers. We've also explored the Events mix-in that Backbone uses to enhance all components with publish-subscribe capabilities and seen how it can be used with arbitrary objects. Finally, we saw how Backbone leverages the Underscore.js and jQuery/Zepto APIs to add rich manipulation and persistence features to Backbone Collections and Models.
2018
2019Backbone has many operations and options beyond those we have covered here and is always evolving, so be sure to visit the official [documentation](http://backbonejs.org/) for more details and the latest features. In the next chapter you will start to get your hands dirty as we walk you through implementation of your first Backbone application.
2020
2021