At the Forge - Backbone.js

Write simple MVC applications in JavaScript with Backbone.js.
Collections and Controllers

Of course, most people have to schedule more than one appointment, which means that this example program needs to keep track of more than one at a time. Now, you normally might assume that you simply could store more than one appointment in a JavaScript array. But, in the world of Backbone.js, you actually use a special kind of object, known as a collection, to store appointments.

Why a collection and not simply an array? Mostly because it works together with other items in Backbone.js. For example, you can set things such that whenever you add or remove an element to your collection, it automatically will invoke another method. For another, collection objects incorporate the Underscore library for JavaScript, which defines a number of methods from functional programming, such as map and pluck, so retrieving information from your collection is quite straightforward.

Just as you defined a model by extending Backbone.Model, you define a collection by extending Backbone.Collection:

Appointments = Backbone.Collection.extend({
  });

Any attributes that you define on the collection are then available, as data or functions, on collection objects of this type. In this particular case, I defined two different attributes, the initialize constructor and the update_appointment_counter method:

Appointments = Backbone.Collection.extend({

  update_appointment_counter: function() {
      $("#number-of-appointments").html(this.length);
  },

    initialize: function(models, options) {
      $("#number-of-appointments").html(this.length);

      this.bind("add", options.view.add_appointment_row);
      this.bind("add", this.update_appointment_counter);
  }

});

In this case, the constructor uses jQuery to initialize the appointment length counter (to zero, given that the collection is only now being initialized) and then adds two handlers to the “add” event. Each time you add a new appointment to this collection, two different functions will fire. One of them (options.view.add_appointment_row) will add a new row to the HTML table containing a list of appointments, and the other (this.update_appointment_counter) updates the counter. As you can see, the functions can be defined in a variety of places; it probably would have made more sense to put both of these methods on the view.

Experienced JavaScript programmers know what “this” is; thus, this.update_appointment_counter makes sense. But, what is options.view? Well, it might help to see how you create your collection, inside the view constructor:

initialize: function() {
  this.appointments = new Appointments(null, {view:this});
},

Basically, you're saying that the appointments attribute for the view is an Appointments collection, starting with no data. Passing a second parameter allows you to set one or more options in a JavaScript object, which is then available as “options”. Because the view passes itself (!) as the “view” option when creating the collection, you then can access the view from within the collection as options.view.

The upshot is that your view, thus, has access to your collection (as this.appointments), and your collection has access to our view (as options.view). This sort of simple, two-way communication is typical for Backbone.js, which tries to make things as simple and short as possible.

The code doesn't include a controller. That's because controllers are necessary only if you want to provide a number of different URLs—well, fragments at the end of a URL—that invoke different methods. For now, you can do without it, but a larger application certainly will require it.

Views

As always in the MVC paradigm, the view is where things are displayed to (and interact with) the end user. In the Rails world, a view is almost always rendered by the controller; your application doesn't need to create it explicitly. In the Backbone.js world, a view is just another object that can be created, often by a model, and which has many controller-like functions. You create it, as you might expect, with:

AppView = Backbone.View.extend({
});

So, you can think of Backbone.js views as fragments of HTML that are added to the current page, plus some of the functionality that you might associate with a controller. Each view is associated with a DOM element. By default, it's a regular “div” element, but you either can set it in one place (using the “el” attribute), or you can set it using a combination of the “tagName”, “className” and “id” attributes as well.

As with models and collections, you can use the “initialize” constructor to set up one or more objects. In the case of this example application, you'll initialize your Appointments collection without any element members, as you saw above when I discussed that collection.

You also will define an event handler, such that clicking on the “add-appointment” button will do so:

events: {
  "click #add-appointment": "add_appointment"
},

When you click on the button, the following code is executed:

add_appointment: function() {
var person = $("#new-appointment td input[name=person]").val();
var meeting_at = $("#new-appointment td 
 ↪input[name=meeting_at]").val();
var note = $("#new-appointment td input[name=note]").val();

this.appointments.add({person: person, meeting_at: meeting_at, 
 ↪note: note});
},

In other words, when you click on the “add-appointment” button, the “click” event handler executes the add_appointment function. This function grabs the values from the little form and uses those values to instantiate a new appointment, adding it to the collection of appointments.

But, you also have event handlers running on the collection! The first handler updates the appointment counter, and the second adds a new row to the table of appointments. It adds the row by cheating a little bit. Although it would have been more elegant to have a second view with an element of “tr” that would add a new row, I decided to mimic some of the on-line tutorials I've seen, adding a new row in a slightly simpler way—namely, an ugly text string.

If I weren't interested in creating an entirely new view, I could have used the “template” function that Backbone.js inherits from underscore.js, giving me ERb-like templates that can be filled in more nicely. Something else that I could have done is break this application into smaller pieces. Although it's nice to have everything in a single file when working on something small, a larger Backbone.js application could well be put into multiple files, with each file defining a different object. Developers experienced with any modern server-side MVC framework, such as Rails or Django, will understand the advantages of putting things into separate files.

______________________

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState