User Tools

Site Tools


eg-259:ch15

~~SLIDESHOW~~

Introduction to Backbone.js

Contact Hour 17: To be discussed on Wednesday 5th March, 2013.

Lecturer: Dr Chris P. Jobling.

An introduction to Backbone based on the Free Udemy course Learn Backbone.js and StackMob by Sidney Maestre with a contribution from Christophe Coenraets.

You can watch the video below in which Sid goes through much the same example at a 2013 San Francisco HTML5 Developer's Meetup. (published by Marakana.tv).

<html> <iframe width=“560” height=“315” src=“http://www.youtube.com/embed/jM8KE_Fa6JI” frameborder=“0” allowfullscreen></iframe> </html>

Introduction to Backbone.js

Please review the Pearltree used in the previous session for some background to MVC and Client-side JavaScript MVC Frameworks.

Contents of this Session

All of the Examples

For convenience, all examples, jsFiddles and source code are linked to backbonejs-hands-on/index.html (web/backbonejs-hands-on/index.html) on the latest update to the eg-259-vm repository.

Some JavaScript Conventions

  • Common pattern – self-calling anonymous function
(function(){
  var foo = bar;
})();

This pattern is used to prevent any code/variables/functions declared inside the body leaking out into the global namespace. Even so, if you omit the var:

(function(){
  foo = bar;
})();

The variable foo would become a global variable1)

Passing in the jQuery variable

(function($){
  var foo = bar;
})(jQuery);

This uses a “closure” to ensure that the jQuery variable is available inside the scope of the anonymous self-calling function as $. This will be true even if $ was redefined after loading this function.

The JavaScript Module Pattern

var app = (function($){
 
    var foo = "bar";
 
    return {
        name : 'module pattern',
        setFoo: function(name) {
            foo = name;
        },
        getFoo: function() {
            return foo;
        }
    };
})(jQuery);

See jsFiddle and 00-module/index.html


Ensures that methods and properties are name-spaced (e.g. app.name, app.setFoo, app.getFoo) to avoid collisions and also that only properties and methods that are made available outside the module. Any dependencies, e.g. in this case jQuery, are passed in as arguments to the self-calling anonymous function.

The Object Namespacing Pattern

var app = app || {};
 
app.model = app.model || {};
app.routers = app.routers || {};
app.collections = app.collections || {};
 
...
app.model.Project = Backbone.Model.extend({});
app.model.ProjectSelection = Backbone.Model.extend({});
app.routers.Application = Backbone.Router.extend({});
app.routers.Projects = Backbone.Collection.extend({});
app.collections.ProjectSelection = Backbone.Collection.extend({});

This is not used in this example project, but is recommended by Addy Osmani in his forthcoming O'Reilly book Developing Backbone.js Applications (Source code and copy, in various e-book formats, on GitHub at addyosmani/backbone-fundamentals). It is a pattern worth considering for your coursework as well.

MVC Defined


  • Model – represents data in the application. May also include behaviour, for example for validation, initialisation, computer properties.
  • View – representation of the data in the web page. Could be several different views: for example models in a list, a single model displayed, a model with controls for editing; an empty view for creating new data items, etc.
  • Controller – traditionally, a mediator for marshalling user interactions and coordinating changes in the model(s) based on user actions and updating views. In most browser-based MVC frameworks, the controller takes on the role of recording the state and views changed based on change events issued by models,

In server-side MVC applications, the controller typically responds to URLs and coodinates the retrieval of model data from databases and pssing them on to HTML templates which are then passed on to the client.

In REST-based applications, the application is much more controller and model-based. Data is typically passed to the browser for rendering as a view. Often, these days, as JSON objects.

Adding Backbone

<!-- Script libraries -->
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://static.stackmob.com/js/json2-min.js"></script>
<script src="http://static.stackmob.com/js/underscore-1.3.3-min.js"></script>
<script src="http://static.stackmob.com/js/backbone-0.9.2-min.js"></script>

Code Review

Models and Collections

  • Defining models
  • Model defualts and methods
  • Change events on models
  • Collections
  • Iteration on collections
  • Change events on collections

Define Model

(function ($) {
 
    Project = Backbone.Model.extend({});
 
    firstProject = new Project({
        title: 'Project 1'
    });
 
})(jQuery);

jsFiddle - 01-model-01

Model Defaults and Methods

    Project = Backbone.Model.extend({
        defaults: {
            title: "Project 1"
        }, 
        updateTitle: function(newTitle) {
            this.set("title", newTitle);
        }
    });
    firstProject = new Project();
    console.log(firstProject.toJSON());

jsFiddle - 01-model-02

Listeners

    firstProject.on('change:title', function () {
        console.log("you've updated the title");
        console.log(this.get('title'));
    });

jsFiddle - 01-model-03

Collections

    Project = Backbone.Model.extend();
 
    Projects = Backbone.Collection.extend({
        Model: Project,
        url: "#"
    });

jsFiddle - 02-collections-01


URL is “RESTful endpoint” which backbone uses to retrive collections.

Collections of Models

projects = new Projects([{
        title: "Project 1"
    }, {
        title: "Project 2"
    }]);
    console.log(projects.toJSON());

jsFiddle - 02-collections-02

Iterating Through a Collection

    projects.each(function (project) {
        console.log(project.get('title'));
    });

jsFiddle - 02-collections-03


each is a method provided by the underscore library. It loops through a collection, returning each element which is then passed on to a function.

Events on Collections

    :
    projects.on('add remove', function (event) {
        console.log("you've changed the collection");
    });
    var firstProject = new Project({
        name: 'Project 1'
    });
    projects.add(firstProject);

jsFiddle - 02-collections-04


In this example, a change event is triggered if an item is added to the collection, or if an item is removed from the collection. In Backbone, wiews can subscribe to such events and redraw the collection should any item change.

Views and Templates

  • Views
  • Templates

Define View

(function ($) {
 
    HomeView = Backbone.View.extend({});
 
})(jQuery);

jsFiddle - 03-view-01


A view (in HTML) is a rectangular area on the page that observes models and collections and updates themselves when the models change. In JavaScript MVC they can also be the target of user interactions and can generate events themselves.

Render View

(function ($) {
    HomeView = Backbone.View.extend({
        render: function () {
            this.$el.append("<h1>My Projects App</h1>");
            return this;
        }
    });
})(jQuery);

jsFiddle - 03-view-02


Render is a function that is used to build the DOM that represents the HTML in the view. Note $el is the jQuery representation of the views element el (which by default is a div).

Initialize View at Startup

$(document).ready(function () {
    projectApp = new HomeView();
});

jsFiddle - 03-view-04


Here we call the view, defined as in the previous slide, on the $(document).ready event that we saw in the session on jQuery.

Adding View Automatically

function ($) {
    HomeView = Backbone.View.extend({
        el: 'body',
 
        initialize: function () {...},
 
        render: function () {...}
    });
})(jQuery);
$(document).ready(function () {
    projectApp = new HomeView();
});

jsFiddle - 03-view-05


By defining the el property to be the body element, the elements contained in the view will automatically be added to the document when it is loaded in the browser. By making the rendered DOM elements the result of processing an HTML template, quite complex user interface components can be built-up from smaller parts. Note that the el property will often be set to a div with an id, as in el: “#contents”.

Multiple Views

    HomeView = Backbone.View.extend({...});
 
    ListView = Backbone.View.extend({
        tagName: 'ul',
        initialize: function () {...},
        render: function () {
            this.$el.empty();
            this.$el.append("<li>Hello</li>");
            this.$el.append("<li>Goodbye</li>");
            return this;
        }
    });

jsFiddle - 03-view-06


Here, we add a ListView, based on an unordered-list, for the display of list items.

Views can render other views

    HomeView = Backbone.View.extend({
        el: 'body',
        initialize: function () {
            this.render();
        },
        render: function () {
            this.$el.empty();
            this.$el.append("<h1>My Project App</h1>");
            this.listView = new ListView();
            this.$el.append(this.listView.render().el);
            return this;
        }
    });

jsFiddle - 03-view-07


Here the home view has been extended and is being used to render the list view inside its own render method. You could render a whole tree of HTML elements in the same way.

Templates

  • Templates extend the power of views by allowing arbitrary pieces of HTML to be stored and rendered at run time by the interpolation of simple fragments of JavaScript.
  • There are several JavaScript templating libraries and Backbone is agnostic about which is used.
  • The default in Backbone is underscore template: _.template.

A brief list would include:

  • Moustache
  • Handlebars
  • Jade
  • EJS

Use Google to find out more.

A Simple Template

In the HTML:

<!-- undescore template ... compiles body then interpolates value at call time -->
<script type="text/template" id="item-container">
  <li><%= value %></li>
</script>

Here we use a script with type of text/template. The browser will ignore this, as the script is not JavaScript, but the browser will still build a script DOM element and Backbone can retrieve its content later by grabbing the content of the $(#item-container') element. Ember.js has a very similar approach, albeit with a different templating engine.

The template syntax is very simple: anything between <%= … %> is considered to be an expression to be output. The content can be any JavaScript statement that returns a value. Another tag pair <% … %> can be used to encapsulate any JavaScript code, e.g. a loop, that doesn't output a value. Te syntax is very similar to the syntax used in server-side templating languages like PHP!

Rendering a Template

ListView = Backbone.View.extend({
 :
 initialize: function () {
   this.template = _.template($('#item-container').html());
 },
 
 render: function () {
    this.$el.empty();
    this.$el.append("<li>Hello</li>");
    :
    this.$el.append(this.template({value: "Hello Backbone"}));
    return this;
 }
});

04-template-01


Here the HTML from the script tag with id item-container is passed to the template function _.template where it is “compiled” into a DOM object witha place-holder for a value. Later, when the render function appends the template to the DOM, the value is passed as an argument to the template and it will then be output into the document.

This simple idea is remarkably powerful and is used in many popular server- and client-side web development frameworks.

A Second Example

        render: function () {
            var el = this.$el,
                template = this.template;
 
            el.empty();
 
            projects.each(function (project) {
                el.append(template(project.toJSON()));
            });
 
            return this;
        }

04-template-02


Here we use a similar template, this time outputting HTML view prepresenting each project in the collection.

Router

Here we have a more complex example using three templates:

  • One for the Project list
  • One for the project detail – including a form for editing the title of the selected project
  • One to render the list items themselves.

Here is the code: 05-router-01

App Router

    AppRouter = Backbone.Router.extend({
        routes: {
            "": "home",
            "add": "add",
            "close": "home",
        },
        home: function () {
            console.log('home');
            new HomeView();
        },
        add: function () {
            console.log('add');
            new AddView();
        }
    });

The purpose of the router is to define some “URL“s that the client will use to navigate around the application. These are simply strings that map a URL to a method. In this example, they map the client-side URLs #, #add and #close to the methods home, add and home respectively. These methods in turn will create, dynamically, a home view and an add view which will respectively display the list of projects and allow a project to be added.

Router Initialisation

$(document).ready(function () {
    projectApp = new AppRouter();
    Backbone.history.start();
});

Here we create a new object representing the whole application as an AppRouter, which itself is defined as an instance of Backbone.Router. We also call Backbone.history.start(); which enables the browser to record the client-side state as URLs that can be retrieved as bookmarks or using the back button.

Events

In the second router example 05-router-02 we add two event listeners to the AddView so that both the add button and pressing enter in the text field will cause an event that will trigger the invocation of the add method that will eventually cause a new project to be added to the collection.

Views Listening to Events

In the final iteration 05-router-03, we add the new project to the project collection and also register an event listener on the ListView so that when a new project is added, a change event is fired and the whole list is redrawn.

Complete CRUD App (in-memory storage)

  • This final version of the app includes a facility for showing existing projects and allowing their titles to be edited.
  • The app now has an UpdateView which supports the editing feature and the list items now include an id in the URL that allows a particular project to be selected for editing.
  • It overrides the Backbone.sync object to allow data to be read from an array of objects in memory rather than a RESTful api (which is it's default behaviour).
  • View the code for insight into how this works

app/index.html

Summary of this Session

What's Next?

Part 3: Server-Side Programming

  • Basic Web Server Operation
  • Interactive Services, CGI and REST

Previous Lecture | home | Next Lecture

1)
In a browser, global variabls and functions are properties of the window objectl.
eg-259/ch15.txt · Last modified: 2013/03/05 20:44 by eechris