Showing posts for tag: backbone

Backbone.js - Events and Controllers

May 9th 2011

In my last post, we discussed Backbone models and views, and how they can form a basic application. Now we’ll discuss how we can leverage Backbone events to allow rich client interaction, and how controllers can be used to create multiple pages within your application.

View events

Let us take the ArticleView from the previous post’s examples:

app.views.ArticleView = Backbone.View.extend({
  events : {
    'click span.remove' : 'remove'
  },

  remove : function () {
    var self = this;
    this.model.destroy({
      success : function () {
        $(self.el).remove();
      },
      error : function () {
        throw self.model.id + ' could not be removed';
      }
    });
  },

  render : function () {
    var out = '<h1>' + this.model.get('title') + '</h1>';
    out += '<p>' + this.model.publishDate() + '</p>';
    out += '<p>' + this.model.get('body') + '</p>';
    out += '<span class="remove">REMOVE</span>' 
    $(this.el).html(out);
    return this;
  }
});

There has been some behaviour added to the ArticleView. A span has been added to which we have registered a click event. Clicking on the text “REMOVE” will result in the model it represents being destroyed on the server. This is performed by Backbone.js sending an HTTP DELETE request to the server. Upon success of this operation, the element in the DOM which represents this model will be removed.

As we can see the events hash can be used to great effect in declaring any behaviour that you wish be be a product of direct user interaction. Events are written in the format {“event selector” : “callback”}.

Model events

Let us refer back to the code that we used to initialise the application in the previous post:

var app = {
  models : {},
  views : {},
  init : function () {
    var articleList = new app.models.ArticleList();
    articleList.fetch({success : function () {
      var view = new app.views.ArticleListView({
        collection : articleList
      });
      view.render();
    });
  }
};

$(document).ready(app.init);

As you can see, we load the article list once on page load, and then never again. However, suppose an editor modifies, adds or removes an existing article, and we wish to update the list in the browser to reflect that new state.

By altering the ArticleListView, we can leverage registration to model events:

app.views.ArticleListView = Backbone.View.extend({
  initialize : function () {
    _.bindAll(this, 'render');
    this.collection.bind('refresh', this.render);
  },    

  render : function () {
    var renderedArticles = _.map(this.collection.models, function (article) {
      return new app.views.ArticleView({model : article}).render().el;
    })
    $(this.el).html(renderedArticles);
    $('#container').html(this.el);
    return this;
  }
});

Now we can change the application initialisation:

var app = {
  models : {},
  views : {},
  init : function () {
    var articleList = new app.models.ArticleList();
    new app.views.ArticleListView({collection : articleList});
    setInterval(articleList.fetch, 10000);
  }
};

$(document).ready(app.init);

As a result, any changes to the article list on the server will be displayed shortly thereafter on the browser. Backbone.js models and collections generate a number of events:

  • “add” (model, collection) — when a model is added to a collection.
  • “remove” (model, collection) — when a model is removed from a collection.
  • “refresh” (collection) — when the collection’s entire contents have been replaced.
  • “change” (model, collection) — when a model’s attributes have changed.
  • “change:[attribute]” (model, collection) — when a specific attribute has been updated.
  • “error” (model, collection) — when a model’s validation fails, or a save call fails on the server.
  • “route:[name]” (controller) — when one of a controller’s routes has matched.
  • “all” — this special event fires for any triggered event, passing the event name as the first argument.

Controllers

It is debatable as to whether Backbone.js controllers can be considered controllers in the context of the common understanding of MVC. They, in fact, bear closer semblance to the rails routes.rb, declaring handlers for URLs. Consider the following example:

app.controllers.AppController = Backbone.Controller.extend({

  routes : function () {
    'articles' : 'index',
    'articles/:id/edit' : 'editArticle',
    'articles/new' : 'newArticle'
  }

  defaultView : function () {
    window.location.hash = 'articles';
  },

  index : function () {
    var articleList = new app.models.ArticleList();
    new app.views.ArticleListView({collection : articleList});
    articleList.fetch();
  },

  editArticle : function (articleId) {
    var article = new app.models.Article({id : articleId});
    new app.views.ArticleEditView({model : article});
    article.fetch();
  },

  newArticle : function () {
    new app.views.ArticleNewView({
     model : new app.models.Article();
    });
  }
});

Here we have an application that not only renders an article list, but also allows the user to create new articles and edit existing ones. Each function on the controller is invoked when the hash fragment of the url matches the url pattern described in the routes hash. When the user has completed an action on any given page, all the view need do is change the url hash to correspond with whatever the user wants or should do next.

Using events and controllers it is possible to provide a user experience which is rich in behaviour and interaction.

var app = {
  models : {},
  views : {},
  controllers : {},
  init : function () {
    new app.controllers.AppController();
    Backbone.history.start();
  }
};

$(document).ready(app.init);

Backbone.js - Models and Views

May 8th 2011

What is backbone?

Backbone.js is a JavaScript framework which brings Model View Controller semantics to your JavaScript application. These semantics allow a rich JavaScript client application to be broken up in meaningful ways. DOM manipulation is separated from business logic by the views and models respectively. Controllers can be used to switch between ‘pages’ within your application.

Models and collections

Backbone provides model and collection classes. For example consider the following:

app.models.Article = Backbone.Model.extend({
  publishedTime : function () {
    return new Date(this.get('publishedDate')).toLocaleTimeString();
  }
});

Here we have a very simple Article class. You’ll notice we have not declared any attributes on this class. This is because backbone models gain their attribute structure from the data used to hydrate them.

Here we have a collection class:

app.models.ArticleList = Backbone.Collection.extend({
  url : '/articles',
  model : app.models.Article
});

As you can see, again the collection class is very anaemic. Both models and collections can be passed to views for rendering, and generate events. In this example the Article list maps to a url which returns JSON in the form:

[ 
  { 
    "id" : 1000,
    "body" : "Bar",
    "publishedDate" : "Mon May 02 2011 16:00:00 GMT+1000 (EST)",
    "title" : "Foo"
  },
  { 
    "id" : 2000,
    "body" : "Bam",
    "publishedDate" : "Fri May 06 2011 15:30:00 GMT+1000 (EST)",
    "title" : "Baz"
  }
]

The object list described here is what provides the models with their structure.

Views

Backbone provides a single view class from which we can extend:

app.views.ArticleListView = Backbone.View.extend({
  render : function () {
    var renderedArticles = _.map(this.collection.models, function (article) {
      return new app.views.ArticleView({model : article}).render().el;
    })
    $(this.el).html(renderedArticles);
    $('#container').html(this.el);
    return this;
  }
});

app.views.ArticleView = Backbone.View.extend({
  render : function () {
    var out = '<h1>' + this.model.get('title') + '</h1>';
    out += '<p>' + this.model.publishedTime() + '</p>';
    out += '<p>' + this.model.get('body') + '</p>';
    $(this.el).html(out);
    return this;
  }
});

Here we see views which can display the article list. The ArticleListView delegates to the ArticleView for rendering of individual elements of the collection. In this example we’re generating the HTML inline, however as your application grows, it’s most likely that you’ll want to extract this to some form of templating system.

The Application

Up until now we’ve only seen models, collections and views on their own. Now we can see how a very basic Backbone.js application hangs together. Here we declare the namespaces, the init function and register it to run on document ready.

var app = {
  models : {},
  views : {},
  init : function () {
    var articleList = new app.models.ArticleList();
    articleList.fetch({success : function () {
      var view = new app.views.ArticleListView({
        collection : articleList
      });
      view.render();
    }});
  }
};

$(document).ready(app.init);

And here we have an extremely basic Backbone.js application. You’ll notice that we have not yet leveraged any events or controllers. In my next post we’ll go over how to use these features to create an application to allow rich user interaction.

subscribe subscribe to this tag

Welcome. Here you'll find Adam Scott's blog and photos. Adam is an agile software developer by trade and a photographer by night.

stuff

related tags