DailyJS

Node Tutorial Part 20: Backbone.js

Alex R. Young

Subscribe

@dailyjs

Facebook

Google+

tutorials server node lmawa nodepad

Node Tutorial Part 20: Backbone.js

Posted by Alex R. Young on .
Featured

tutorials server node lmawa nodepad

Node Tutorial Part 20: Backbone.js

Posted by Alex R. Young on .

Welcome to part 20 of Let's Make a Web App, a tutorial series about
building a web app with Node. This series will
walk you through the major areas you'll need to face when building your
own applications. These tutorials are tagged with
lmawa.

Click to show previous tutorials.

Backbone Persistence

I haven't yet hooked up our interface and models to Backbone's
persistence layer. I generally work by relying on
model.save(attributes) and model.destroy().
The save method knows when to create or update based on if
the id attribute has been set -- remember this, because
prior to this tutorial Nodepad was using _id which
confuses Backbone.

The add/remove document toolbar could use a skeleton Backbone view like
this:

ListToolBar = Backbone.View.extend({
  el: $('#left .toolbar'),

  events: {
    'click #create-document': 'add',
    'click #delete-document': 'remove'
  },

  initialize: function(model) {
    _.bindAll(this, 'add', 'remove');
    this.model = model;
  },

  add: function(e) {
    // TODO: Create a new document
  },

  remove: function(e) {
    e.preventDefault();
    if (confirm('Are you sure you want to delete that document?')) {
      this.model.destroy();
    }
  }
});

The destroy method will delete documents using a HTTP
DELETE, the same way as my previous jQuery implementation.
We actually need to instantiate ListToolBar with a model
though, so where should that happen? I decided to put it in the
DocumentRow view:

open: function() {
  $('#document-list .selected').removeClass('selected');
  $(this.el).addClass('selected');
  this.model.display();
  this.toolbar = new ListToolBar(this.model);
}

Every time a document is selected, a toolbar will be instantiated. Now
there's a relationship between the toolbar view and the current
document.

We still need to create a new document when the +
button is pressed... This is basically a case of instantiating a
document and calling save:

add: function(e) {
  e.preventDefault();
  var d = new Document({ title: 'Untitled Document', data: '' });
  d.save();

  // Add it to the collection
  Documents.add(d);

  // addDocument is a new method I've added to DocumentList which just appends
  // the right elements to the unordered list
  appView.documentList.addDocument(d);

  // Trigger an open
  d.rowView.open();
}

There's some housekeeping going on there, but notice that we basically
just call save to make Backbone do all the boring Ajax work
for us.

DocumentControls

I've also added a view called DocumentControls which
manages updating documents. By now nothing in this should really
surprise you:

DocumentControls = Backbone.View.extend({
  el: $('#controls'),

  events: {
    'click #save-button': 'save'
  },

  initialize: function(model) {
    _.bindAll(this, 'save', 'showHTML');
    this.model = model;
  },

  save: function(e) {
    this.model.set({
      title: $('input.title').val(),
      data: $('#editor').val()
    });

    this.model.save();
    this.model.rowView.render();
    e.preventDefault();
  },

  showHTML: function(e) {
    e.preventDefault();
    // TODO
  }
});

The line that reads this.model.rowView.render() is just
triggering the DocumentRow to update its contents. For
clarity, it reads like this:

render: function() {
  $(this.el).html(this.template({
    id: this.model.id,
    title: this.model.get('title')
  }));
  return this;
}

Conclusion

I hope it's now clear that working with Backbone and REST APIs can be
less work than a mess of Ajax calls and CSS selectors.

This week's code was commit
7d5cc3d
.