Backbone Reactive – Adding in Events

This is Part Three of the Backbone Reactive Tutorial. For all sections, please view the Table of Contents.

So far the List page doesn’t do anything apart from list the messages we have. This means that it is less functional than the static html fallback site – at least with that one you could view the message contents. So in this post we are going to build on our simple Backbone app, adding in events so you can actually view the messages.

Luckily adding Events in Backbone is simple. Within the ListRowView all you have to do is add:

  events: {
    "click": "open"
  },
 
  open: function () {
    console.log("clicked: " + this.model.id);
  },

Now when you reload the page and click on a row you should see the console output the ID of the model you clicked on. So now we know we can click on a row, we need to load in the data and display it.

Note: You can find all the source code to this project on GitHub, this chapter has been tagged ch3-events

Let’s tackle this in reverse order and create a new view to display the message contents, we’ll call it MessageView:

var MessageView = Backbone.View.extend({
  id: 'message',
 
  render: function () {
    var date = DateFunctions.getDate(this.model.get('date'));
    var dateNice = DateFunctions.getDateAndTimeNice(date);
 
    var html = '<p><label>From</label> ' + this.model.get('from') + '</p>' +
      '<p><label>Date</label> ' + dateNice + '</p>' +
      '<p><label>Subject</label> ' + this.model.get('subject') + '</p>' +
      '<p>' + this.model.get('body') + '</p>';
    this.$el.html(html);
    // if the message view is not already attached to the page, simply
    // append it into the body
    if (!$("body").children("#" + this.id).length) {
      $('body').append(this.$el);
    }
  }
});

There is nothing too special going on here. All this view currently does is to render a bunch of HTML, using the properties of its model for the values.

You may spot a new object called DateFunctions. This was a simple utility object created as now we are displaying the data in two views (the ListRowView and the MessageView).

The big questions about this view are:
– how does it get the data to display? and
– where is it called from?

When working through this initially I started to initiate and load the MessageView directly in ListRowView when the event was clicked (see /57e9e5f/). However, this lead to the two undesirable outcomes.

Firstly MessageView would have been repeatedly initialised every time someone tries to view a message. This isn’t too big a deal, but it is not efficient when we could initialise once and be done with it.

Secondly, as the MessageView would be initialised every time we want to view the message, when the view renders and injectes itself into the DOM we needed to add logic to stop it adding itself multiple times to the page.

There are a couple of ways to solve this. One method (although I didn’t try it) would possibly be to create a singleton class for the Message viewer, this would have stopped the reinitialisation issue.

The method I went for in the end was to “bubble” the event up, from the List Row to the main List view. As all the ListRowViews would bubble up the event, we would only need to call the MessageView once and then listen for the “view message” event to re-render the message viewer.

We use the Backbone trigger functionality to bubble up the event. (Recall that the open function is called when a row click event is detected).

  open: function () {
    this.trigger("viewMessage", this.model);
  }

Now, in the ListRowView we make two changes. Within the initialize function we initialize MessageView.

  initialize: function () {
    ...
    this.messageView = new MessageView();
  },

And then during the rendering process, when looping through the listItems we add a listener to the ListRowView instance and when a viewMessage event is detected, we instruct the MessageView instance to load the message based on the model passed with the event.

    self.collection.each(function (listItem) {
      var v = new ListRowView({model: listItem});
      v.render();
      // listen for the "viewMessage" event, loading the message whenever it is triggered
      v.on('viewMessage', function (model) {
        self.messageView.loadMessage(model);
      });
 
      self.$el.children("table").append(v.el);
    });

The method to load the message within MessageView is very simple, all it does is to tell the model passed to (re)fetch it’s data and then renders the view.

var MessageView = Backbone.View.extend({
  id: 'message',
 
  loadMessage: function (model) {
    var self = this;
    self.model = model;
    self.model.fetch({success: function () {
      self.render();
    }});
  },
 
  ...
});

Finally, to finish this all off we must update the ListItem model ever so slightly, informing it where it should load in the data, by setting the URL. This is slightly more complicated than the ListCollection URL as we need to specify which message we want to load in, so have to add the model’s ID to the URL.

var ListItem = Backbone.Model.extend({
  url: function () {
    return '/mock-data/message.' + this.id + '.json';
  }
});

An example of the mock data we will load in is:

{
  "id": 1,
  "date": "1357643063000", 
  "subject": "Testing the Backbone Reactive List Page", 
  "from": "Rob" ,
  "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque ornare, ligula a tincidunt cursus, lorem magna ullamcorper enim, ut interdum nisi quam vel lorem. Aliquam aliquam sem eu ipsum pretium ut bibendum quam blandit. Duis vel blandit nibh. Vestibulum mattis sapien eros, ut iaculis quam. Suspendisse potenti. Ut eleifend massa a libero adipiscing porttitor in ut nunc. Sed lobortis pretium consectetur. Morbi convallis, velit sit amet placerat vehicula, dolor lorem sollicitudin enim, at convallis tortor ipsum vel lectus. Fusce eget tellus sit amet nisl interdum volutpat. Etiam congue nibh id quam scelerisque posuere."
}

Conclusion

We are really starting to get to the meat of Backbone JS now. The ability to automatically get fresh data from the backend (or in our case the mock json data) and then the views to be updated without having to worry about JQuery calls or complex logic is really powerful stuff.

Reminder: you can find all the source code to this project on GitHub, this chapter has been tagged ch3-events.