Lets Talk – An EventBus in Backbone.js

A few days back, we ran into an issue on a project using “Backbone.js”:http://documentcloud.github.com/backbone/ where we needed two Backbone views to be able to talk to one another. This is not an issue when there is a view A that has a reference to view B and binds to any events that propagate from B. Our issue was a little more in-depth because these views did not have references to each other, they only shared a model. We discovered two ways to handle this communication issue: the hack and the right way.

h2. The Setup

We have a main view rendering a preview page view as well as individual item views. The main view gives the preview view a collection of items to generate and show item previews. The previews include a title, description and comment count of the items. When a user clicks a preview view to see the full version, the main view builds the full item view and swaps it in on the page (in place of the preview page). We hold copies of the full item views and the preview view in memory to allow easy swapping between pages without needing to re-render.

h2. The Problem

When we load the full item view, we then load in all the comments associated with it. We display the comments on the page and fill in a comment count on the full view. But wait, didn’t we already have a comment count for the preview item? We did, but that was only a static attribute on the item. In the full item view, when we add or remove a comment, the comment count changes to reflect the actual count. With this setup, we never updated the comment count on the preview. Since our app swaps out the preview page and full item pages via JavaScript with the JSON provided on the full page load, that static comment count becomes stale.

h2. The Fixes

Our goal was to give the comment collection to the item preview view when we pulled it from the server during the full item view creation. This would allow the preview view to stay up-to-date with comment count changes and keep the UI consistent. The only thing these view have directly in common was the item model (remember that the item previews were children of the preview view, which is a sibling of the full item views).

Initially our solutions were ugly. We could have the preview view bind to an event on the model and the full view trigger that event on the model, passing along the comment collection. This sounds reasonable, except that we are binding and triggering on a model that has no notion of this. It worked, but it felt dirty to just happen to have a common model to pass data over. More importantly, it would cause rework if we used two different models to render the full view and the preview view – suddenly there is no shared model.

The next idea was to pass the comment collection, after it loaded, up to the master view. The master view would then have to pass the collection to the preview view, which would then pass it down to the item preview view. This solution would work too, but would be a headache to implement. We had done this elsewhere, and it was time consuming. It not only took time, it also posed a risk if we were to add or remove a view in the chain – breaking the data sharing.

h3. The EventBus

Backbone.js has events and event binding without a notion of a shared communication channel like an EventBus. Our final solution to our issue (surprise!) was to implement one. Below is our setup.


MyApp.EventBus = _.extend({}, Backbone.Events)

Simple, huh? In our application namespace we extended an empty object (with the power of “underscore”:http://documentcloud.github.com/underscore/) to create a “Backbone.Events”:http://documentcloud.github.com/backbone/#Events object. This allows us to simply bind and trigger on the object from anywhere in the app.


vent = MyApp.EventBus

vent.bind 'foo', -> console.log 'bar'

vent.trigger 'foo'
Conversation
  • Sam says:

    Wow, what a great idea…it’s so obvious now that I see it. Thanks!

  • Roy Kolak says:

    I’m curious about your problem, but I’m finding it hard to build in my head from your description (I like pictures). Do you think you could post a diagram of your view/model/collection structure?

  • Roy Kolak says:

    Excellent picture, thanks! I don’t think I can wrap my head around creating a separate message bus for this case. It’s seems like binding on change to the comment collection and the shared model is enough. Here’s what I’m thinking:

    Bind on change to the comment collection. In the Specific Item View, when a user removes/adds a comment, the callback can update the cached comment count on the shared model.

    Each model in the Collection View can bind to change on the cached comment count property and on callback, update their comment counts in the view.

  • Roy Kolak says:

    Excellent picture, thanks! I don’t think I can wrap my head around creating a separate message bus for this case. It’s seems like binding on change to the comment collection and the shared model is enough. Here’s what I’m thinking:

    Bind on change to the comment collection. In the Specific Item View, when a user removes/adds a comment, the callback can update the cached comment count on the shared model.

    Each preview view in the Collection View can bind to change on their model’s cached comment count property and on callback, update their comment counts in the view.

    • Jared Sartin Jared Sartin says:

      One problem is that the comment view doesn’t have reference to the item model (in our real world app). This was an idea was one we considered, but felt that we shouldn’t allow a comment view to modify an item model directly (not it’s responsibility). I seem to have left this restraint out.

      • Roy Kolak says:

        Ahh, ok. I thought the shared model that was referred to in the first paragraph was the model passed to the Specific Item view and each Preview Item view in the Collection View. If that was the case, it would function as a nice context specific communication channel from the top level model summary to the guts (the comment collection) it summarizes all while the views bind to changes in this channel.

  • Anon says:

    Was this really “your” solution? Are you not giving credit where credit is due?

    http://lostechies.com/derickbailey/2011/07/19/references-routing-and-the-event-aggregator-coordinating-views-in-backbone-js/

    • Jared Sartin Jared Sartin says:

      We came to this solution while looking for more help online. We did find Mr. Bailey’s solution and used his recap post to tailor our events for namespacing. I did include BOTH his posts at the bottom, referencing that he has a similar problem/solution and his recap is much more in-depth.

      I do not rip other’s work off and claim it as my own. This solution can be done in many ways, and this is the most simple and common way. We could have also fixed this issues with:

      MyApp.EventBus = _.clone(Backbone.Events)

      Which is available on Backbone.js documentation, and somehow was missed during my reading.

      Thank you for pointing out Mr. Bailey’s post. He is an awesome blogger to follow, for those in the JS community. I did find him after we had settled on our solution and have really enjoyed his other posts too.

  • […] April 16th – Let’s talk; An EventBus in Backbone.js […]

  • Comments are closed.