We're hiring!

We're actively seeking designers and developers for our all three of our locations.

Lets Talk – An EventBus in Backbone.js

A few days back, we ran into an issue on a project using Backbone.js 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.

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.

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.

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.

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) to create a 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'

The solution is much like Derick Bailey’s setup. Here is a look at his journey, the first post is about solving the problem and the second is a look back at the solution and how to improve it. The second post is full of great pointers on our common solution and how to get better results from it.

  1. References, Routing, And The Event Aggregator: Coordinating Views In Backbone.js
  2. Revisiting The Backbone Event Aggregator: Lessons Learned
Jared Sartin (33 Posts)

Web and mobile developer. Works in Ruby/RoR and JavaScript frameworks.

Strong passion for game development, UI design, and general UX.

This entry was posted in Web Apps and tagged , , . Bookmark the permalink. Both comments and trackbacks are currently closed.

9 Comments

  1. Posted April 16, 2012 at 11:09 am

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

  2. Posted April 16, 2012 at 11:20 am

    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?

    • Jared Sartin
      Posted April 16, 2012 at 2:35 pm

      We updated the post with an image!

  3. Posted April 16, 2012 at 2:55 pm

    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.

  4. Posted April 16, 2012 at 3:17 pm

    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
      Posted April 16, 2012 at 5:13 pm

      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.

      • Posted April 16, 2012 at 11:33 pm

        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.

  5. Anon
    Posted April 16, 2012 at 3:22 pm

    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
      Posted April 16, 2012 at 5:18 pm

      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.

One Trackback

  1. By Improved Clickthroughs Rates | Atomic Spin on April 29, 2012 at 9:33 pm

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