Backbone.js Overview

http://seattlejs.training.formidablelabs.com

@FormidableLabs | formidablelabs.com

Tip - space bar advances slides

Web App Goals

  • Complex, Data-Driven
  • Interactive, Low-latency
  • Maintainable

To meet these goals, apps are rapidly shifting to the browser.

The frontend is getting big and complex.

Solutions

  • Organize frontend JavaScript.
  • Use architecture / design lessons from the backend.

MVC

Separate and organize around:

  • Data modeling / communication / persistence
  • Display rendering / UI
  • Brokering data, display, DOM

Frameworks

Many good, modern tools.

Why Backbone.js?

  • Compact
  • Flexible
  • Comprehensible
  • Battle Tested

Backbone as MV...C?

  • M - models, collections
  • V - templates and views
  • C - views, routers

Backbone.js Topics

  • Events
  • Models / Collections
  • Views
  • Templates (Handlebars)
  • Routers

Events

Backbone.Events emit & respond to DOM, Backbone.js, and custom events.


Events Methods

  • Listeners: on, off, once, listenTo, listenToOnce
  • Triggers: trigger

Events Demo


                var eventer = {};
                _.extend(eventer, Backbone.Events);
                
                eventer.on("foo", function (msg) {
                  console.log("FOO:", msg);
                });
                
                eventer.trigger("foo", "hi");
                

Models

A Backbone.Model contains, retrieves, and manipulates the data for your app.


Models

var HelloModel = Backbone.Model.extend({
  urlRoot: "/path/to/hello", // ":id"
  defaults: {
    message: "hiya"
  }
});

Model Methods

  • Accessors: get, set
  • Persistence: sync, fetch, save, destroy
  • Network: id, url, urlRoot, parse

Models Demo


                var HelloModel = Backbone.Model.extend({
                  defaults: {
                    message: "hiya"
                  }
                });
                
                var hello = new HelloModel();
                console.log("Defaults:", hello.get("message"));
                
                hello.set("message", "Hey there");
                console.log("Set:", hello.get("message"));
                

Your Turn Demo


                var SandwichModel = Backbone.Model.extend({
                  defaults: {
                    // TASK: Add sandwich default attributes
                  }
                });
                
                var sandwich = new SandwichModel();
                sandwich.on("change", function () {
                  // TASK: console.log your sandwich order.
                });
                
                // TASK: set new sandwich attributes
                

Solution Demo


                var SandwichModel = Backbone.Model.extend({
                  defaults: { bread: "white", cheese: "cheddar" }
                });
                
                var sandwich = new SandwichModel();
                sandwich.on("change", function () {
                  console.log("I want a sandwich with " +
                              JSON.stringify(sandwich.toJSON()));
                });
                
                sandwich.set({ cheese: "swiss", meat: "pastrami" });
                

REST / Backend

Models assume JSON data via REST.

But configurable! (localStorage, etc.)

var HelloModel = Backbone.Model.extend({
  urlRoot: "/path/to/hello", // ":id"
});

Collections

A Backbone.Collection is an ordered list of models.


  

Collections

/*global HelloModel */
var HelloCollection = Backbone.Collection.extend({
  model: HelloModel,
  // Use localStorage instead of REST.
  localStorage: new Backbone.LocalStorage("bb-col-demo")
});

Collection Methods

  • Accessors: get, at, set, push, pop, shift, ...
  • Persistence: sync, fetch
  • Network: url, parse
  • Iteration: Underscore methods, filter, findWhere.

Collections Demo


                var HelloCollection = Backbone.Collection.extend({
                  localStorage: new Backbone.LocalStorage("bb-col-demo")
                });
                var hellos = new HelloCollection();
                // CLEAR: hellos.localStorage._clear();
                // FETCH: hellos.fetch({ reset: true });
                
                _.each(["Hi", "Hello", "Hola"], function (msg) {
                  hellos.create({ message: msg });
                });
                
                hellos.each(function (model, i) {
                  console.log(i, ":", model.get("message"));
                });
                

Your Turn Demo


                var HelloCollection = Backbone.Collection.extend({
                  localStorage: new Backbone.LocalStorage("bb-col-demo")
                });
                var hellos = new HelloCollection();
                hellos.fetch(); // Use existing models!
                
                // TODO: Print out greetings containing an "o"
                // HINT: There's a useful underscore method
                

Your Turn Demo


                var HelloCollection = Backbone.Collection.extend({
                  localStorage: new Backbone.LocalStorage("bb-col-demo")
                });
                var hellos = new HelloCollection();
                hellos.fetch(); // Use existing models!
                
                hellos.chain()
                  .filter(function (model) {
                    return model.get("message").match(/o/);
                  })
                  .each(function (model, i) {
                    console.log(i, ":", model.get("message"));
                  });
                  

REST / Backend

Collections also assume JSON data via REST.

/*global HelloModel */
var HelloCollection = Backbone.Collection.extend({
  urlRoot: "/path/to/hello",
  model: HelloModel
});

Templates

A function that renders model / collection object data as HTML.


Templates

Backbone.js is agnostic as to how to implement templates or which library to use. We'll use Handlebars.js

Handlebars Demo


                var template = Handlebars.compile(
                  "

{{title}}

\n" + "

{{{msg}}}

" ); console.log("Rendered:\n", template({ title: "Hello HBS!", msg: "HBS is awesome!" }));

Your Turn Demo


                var Person = Backbone.Model.extend();
                var oscar = new Person({
                  firstName: "Oscar",
                  lastName: "The Grouch"
                });
                
                var templateText = null; // TODO: Create a greeting
                var template = null; // TODO: Compile the template text
                
                // TODO: Say "Hi" to Oscar's full name.
                // HINT: Use `model.toJSON()`
                

Solution Demo


                var Person = Backbone.Model.extend();
                var oscar = new Person({
                  firstName: "Oscar",
                  lastName: "The Grouch"
                });
                
                var templateText = "Hi, {{firstName}} {{lastName}}!";
                var template = Handlebars.compile(templateText);
                
                console.log(template(oscar.toJSON()));
                

Other Topics

Views

A Backbone.View mediates data, event and display logic.


View Methods

  • Declare: el, events, template
  • UI: render, remove
  • Helpers: $el, $

View Basics

/*global HelloModel, HelloTemplate */
var HelloView = Backbone.Model.extend({
  template:   HelloTemplate,
  model:      HelloModel,
  initialize: function () { /* SETUP  */ },
  render:     function () { /* RENDER */ }
});

Views Demo


                var FooView = Backbone.View.extend({
                  template: Handlebars.compile("

Hi {{name}}!

"), model: new Backbone.Model({ name: "Bob" }), initialize: function () { this.listenTo(this.model, "change", this.render); }, render: function () { this.$el.html(this.template(this.model.toJSON())); return this; } }); var fooView = new FooView({ el: "#cm-output" }); fooView.render(); // TRY: fooView.model.set("name", "Bill");

Scoped Selectors

  • Global: $(".foo")
  • Scoped: this.$(".foo") (this.$el.find())

Routers

A Backbone.Router routes client-side pages and mediates history.


Hash Fragments

  • http://myapp.com/#my-route
  • http://myapp.com/#my-other-route

Real URLs (Push State)

For browsers with pushState support.

  • http://myapp.com/my-route
  • http://myapp.com/my-other-route

Router Methods

  • Declare: routes
  • Call: navigate

Routes

var Router = Backbone.Router.extend({
  routes: {
    "foo": function () { console.log("foo"); },
    "bar": "handleBar"
  },
  handleBar: function () {
    console.log("bar");
  }
});

Route Parameters

var Router = Backbone.Router.extend({
  routes: {
    "foo/:id": "handleFoo",
    "bar/*everything": "handleBar"
  },
  // url "#foo/10"
  handleFoo: function (id) { // id gets 10
    console.log("foo has id " + id);
  },
  // url "#bar/then/some/path"
  handleBar: function (everything) { // "then/some/path"
    console.log("bar");
  }
});

Routers Demo


                var Router = Backbone.Router.extend({
                  routes: {
                    "foo": function () { console.log("ROUTE: foo"); },
                    "bar": function () { console.log("ROUTE: bar"); }
                  }
                });
                var router = new Router();
                
                Backbone.History.started || Backbone.history.start();
                
                // router.navigate("", { trigger: true });
                console.log("START: " + window.location.hash);
                router.navigate("foo", { trigger: true });
                console.log("END:   " + window.location.hash);
                

Challenges (1)

  • View uses a collection instead of a model. Iterate collection items in a new Handlebars template.
  • View uses a collection and instead of rendering a template, creates children views for each model instance. Render and attach children to DOM.
  • Bind a click handler to your child view to do some action on clicking individual child views -- such as changing the background color.

Challenges (2)

  • Implement a "single item view" that corresponds to one element in the collection. (Hint: Add a unique id field to your model or use collection.at(INDEX)).
  • Implement a router to route between "all elements" view and "single element" view by unique identifier on URL.
  • Add clickable DOM elements to the "all elements" view to switch to "single element" view for each element using the router.

Backbone.js


backbonejs.org

@FormidableLabs | formidablelabs.com