PageRenderTime 8ms CodeModel.GetById 4ms app.highlight 77ms 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

Large files files are truncated, but you can click here to view the full 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 …

Large files files are truncated, but you can click here to view the full file