Get to Grips with Backbone Events

Events are great, aren't they? People, beverages, interesting conversation. But what about Backbone events? Well, unlike social events, Backbone events are even cooler as they can bring your site to life. They listen for something to happen and then trigger something to happen because of it. In SCA, we commonly use them on forms (ie to trigger CRUD methods) and for manipulating visual elements (ie clicking a button to advance a slider).

Let's take a look at what events are, how we use them, and the possible ways you can make use of them. To do this, we're going to find fun ways to pester users with offers of friendship.

Binding Events

In Backbone, events are provided by a module named Backbone.Events in Backbone.js. To quote the Backbone documentation:

Events is a module that can be mixed in to any object, giving the object the ability to bind and trigger custom named events. Events do not have to be declared before they are bound, and may take passed arguments.

Events can be bound to models, collections and views.

View-Only Events

First, let's take a look at some functionality available only to views. After extending the view, you can specify events that you want to be bound. One way to do this is like the following:

, events: {
    'click': 'annoyUser'
  }

, annoyUser: function()
  {
    alert('Can we be friends?')
  }

I've added this to a view. Now, whenever I click anywhere within that view, the following happens:

Goodness. What a horrible thing to happen to a user. But still, this is a very basic example and gives you an idea of what's capable: when a user clicks, show them a message. Easy.

This way is specifically for DOM events, which is provided by jQuery's on function and follow the pattern of {"event selector": "callback"}.

For more information, take a look at the view events documentation.

General Events

More generally, however, there's also another way to bind events. We can't replicate the above example, because monitoring clicks is not something you can do this way (how could a user click on a model?), but we can be extra annoying by doing the following:

, initialize: function initialize() {
    this.on('all', this.annoyUser());
  }

Oh the humanity. Now whenever anything happens to this view, it will annoy the user. Seeing as this view is called five times in my collection, it will pop up five times.

For more information on this, see the events documentation.

Real Examples

To get a better understanding of events, let's take a look at how we use them in SCA.

Forms

I mentioned at the start that we use them in forms, and this is a good place to start. As you may know, when using a form in SCA we use the custom-built Backbone.FormView.js file, which is a standard way of handling the submission and validation of form data. So, after requiring it as a dependency in our form view and initializing our view as a form view, we're able to use its methods (specifically, we want the saveForm method) by doing the following:

, events: {
    'submit form': 'saveForm'
  }

What we're doing here is connecting the physical act (close enough) of submitting a form with the logical act. If we weren't using Backbone then we might use jQuery, or maybe even one of the many native ways of doing it.

Note that within the first part of the two parts of each entry in the events hash are a further two parts: the event and the selector. Thus, in the above example, submit form is only coincidentally a plain-English description of what's happening, it is, infact, an event and a selector: submit (event) form (valid jQuery selector). Instead of form we could have easily chosen something like an ID.

Star Rating (Custom Events)

Don't forget that as these events are accessible by jQuery, you can use the events property in your view to create custom events, like you would in jQuery. Custom events are useful for a couple of reasons:

  • The event may be triggered by numerous things (eg, not necessarily clicking a single element)
  • You can demarcate the code, so it's crystal clear you're calling your custom event

We use this in our star rating functionality: when a user clicks on a star to set their rating (eg a product, out of five) it triggers a callback to the rate event that does a bunch of heavy lifting.

In our example, we can do a similar thing. Keep our new method, but change the event to the following:

, events: {
    'friends': 'annoyUser'
  }

So here you can see we've switched out 'submit form' for something called 'friends': as there is no standard event called that, it'll be created. In fact, when we go back to our page and look at the event listeners for the containers, we can see that they've been bound to it.

Thus, the code we've entered above is logically similar to something like this:

jQuery('.myreviews-detail-container').on('friends'), function() {
  alert('Can we be friends?');
}

To trigger it, we need to use the companion method to .on(), namely .trigger(). For example, in your console you can write the following:

jQuery('.myreviews-detail-container').trigger('friends');

This is obviously getting a bit far from practical applications, but you should be able to see how it all fits together in Backbone and therefore becomes applicable.

Listening to Data Operations

So we've talked a bit about forms but let's look a little deeper. When dealing with user information, there are typically four operations we can perform:

  1. Create — adding information by creating a new record
  2. Read — request existing information from the server
  3. Update — request existing information from the server but send back changes to it
  4. Delete — request existing information from the server but send back a request to remove it

As Backbone is driven by a need to process and pass data, we can listen to when the above methods happen and then perform actions on them.

One common way that we might use event listeners is to aid the user's experience when they are finished with a form. In this example, a user has just completed the details (either creating or updating a record) and no longer needs to see the form. Some UX designers like to have a confirmation appear saying that the data has been successfully sent to the server. However, what we can do is have the router navigate the user back from the details view to the list view.

So, for example, below the code that calls the view to show its content again, we could add in:

view.model.on('sync change destroy reset add', function (model)
{
  Backbone.history.navigate('formList', {trigger: true});
  console.log('Hey friend, I see you\'re on the view list');
});

In this example, you'll need to substitute formList for the name of route you want the router to process; assuming that this points to the route address for the list view, it'll navigate the user automatically back to it. You could, of course, simply set this to the home of the current application you're in by changing it to a slash (/) — hey, you could even put any route in.

Listening for Other Backbone Events

Indeed, the .on() method is very powerful when paired with a number of events. In this case, the router is being told to create a model and then create an event listener for it.

Using .on() pops up frequently in our source code and has a lot of uses. For example, if you want something to happen after your composite view has rendered, you could add in something like this:

this.on('afterCompositeViewRender', function () {
  console.log('I waited for the composite view before saying hi')
});

But we can also listen to the events of other objects using .listenTo(). For example, if you wanted to have a callback in your list view that triggers when the collection has been updated, you could add the following to your initialize function:

this.listenTo(this.collection, 'sync change destroy reset add', console.log('I see you updated the collection, can we be friends now?'));

So this is very similar to the view.model.on code above, but there's an added benefit of doing it this way: this object keeps track of the events, and they can be removed later on using the .stopListening() method. Just keep in mind that if you use this, what it refers to can change quite rapidly.

In SCA, we don't actually make use of .listenTo() or.stopListening() but we do use .on() and .off() a fair bit (ie we tell objects to start and stop listening to events that happen to itself). For example, if you've created a list view and you want to re-render the view when the collection changes (ie a model is added/deleted/modified) then you can just add the following:

var self = this;
this.collection.on('reset sync add remove change destroy', function() {
  self.render();
});

In other words, "listen to the collection and when something to do with data happens, re-render the list view".

Final Thoughts

Events are an essential part of how we use Backbone in SCA. We have forms, and clicking a button needs to have an impact in the code. Thus. using the events module that comes with Backbone is a shorthand way of doing this without having to explicitly involve jQuery.

Similarly, when we use objects (ie models, views and collections) it's super handy to listen to things that happen to them. A model got removed? Well let's re-render the list then. View finished rendering and want to inject some additional code? Go for it.

Have you made uses of events? What have you achieved with them?