References, Routing, And The Event Aggregator: Coordinating Views In Backbone.js

I recently found myself needing to facilitate communication between two backbone views. The first view is a medication – one that is currently being taken by a particular patient. The second view is the add/edit view that allows the patient to either add new medications or edit existing ones. When the edit icon is clicked for an existing medication, the edit form should be populated and the user should be able to edit the medication.

Here’s what the screen looks like after I click on the edit icon (the pencil):

Screen shot 2011 07 19 at 9 02 38 AM

There are a number of ways to make this work – the most basic of which is to have the views reference each other so that they can either call methods on each other or raise events.

References

I’ve done this a number of times and it works well.

MedicationView = Backbone.View.extend({
  events: {
    "click #edit": "editMedication"
  },

  editMedication: function(){
    var editView = new AddEditView({model: this.model});
    editView.render();
  }
});

In this simple example, I have an event setup that listens to the edit icon click. When it’s clicked, I instantiate a new add/edit view with the model from the current medication display view. I then render it, and we’re on our way – the add/edit view will display the correct model and life is good.

In some cases, though, this isn’t really an option – or, it may be an option that would cause a little extra code and work to make sure it works correctly. The screen shot above is one of those cases. Both the “Add Medication” button and the add/edit form are part of the same view. I did this because it did not seem necessary to have a view just for the “Add Medication” button when something as simple as a jQuery click event would suffice. Rather than code the jQuery on it’s own, though, I decided to put the “Add Medication” button into my add/edit view. It makes sense to me – the button allows you to add a new medication, so it should be part of the add/edit view.

Because of the choice I made, though, having the edit button for a medication instantiate a new add/edit form was not an option for me. I could have the medication view reference the existing instance of the add/edit view, though.

MedicationView = Backbone.View.extend({
  events: {
    "click #edit": "editMedication"
  },

  initialize: function(options){
    this.addEditView = options.addEditView;
  }

  editMedication: function(){
    this.addEditView.render();
  }
});

// initialize everything here... 
// 'medicationList' is a collection of medications
var addEditView = new AddEditView(...);

medicationlist.each(function(med){
  new MedicationView({model: med, addEditView: addEditView});
}

This also works, giving me the functionality i need with one instance of the add/edit view. Not being satisfied with what works, though, I wanted to explore my other options to see what else would work. Specifically, I wanted to see if I could completely decouple these two views and still provide the functionality that I needed.

Routing

My next thought was to take advantage of the routing in backbone. I thought I could have the edit button changed from a clickable button to a simple <a href=”#edit/id”> link. I could then have the router pick the correct model from the collection and pass it to the add/edit view.

MedicationRouter = Backbone.Router.extend({
  routes: {
    "edit/:id": "editMedication"
  },

  editMedication: function(id){
    var med = medicationList.get(id);
    this.addEditView.editMedication(med);
  }
});

AddEditView = Backbone.View.extend({

  editMedication: function(medication){
    this.model = medication;
    this.render();
  }

});

Once again, this would work perfectly fine. The router would pick up the change to the #hash url, use the id passed into it as the parameter to load the medication, and then send it over to the add/edit view.

However, I don’t need all of the functionality of the router in this case. I don’t need the browser back / forward button. I don’t need a url change to create some uniquely identifiable url for a user to hit directly, and I don’t need a router for anything else on this page. Since all of the medications are listed on the page and the add/edit view is on the page with them, there’s no need for me to route things around. Sure it works, but it comes with more functionality than I wanted.

The Event Aggregator

Digging back into my Winforms development days of the last 4 years, I decided to go after a tried and true pattern that I’ve used more times than I can remember: the event aggregator. If you’re not familiar with event aggregators, here’s a handful of links that talk about them in-depth including my own blog post posts and sample code for Winforms (C#/.NET).

The gist of the event aggregator is that you have a central object that manages the raising of events and the subscribers for those events. In terms of messaging patterns, the event aggregator is an in-memory, object based publish-subscribe model. It allows you have to have disparate parts of your system react to the events of other parts of the system, without having them directly coupled. I use event aggregators in my winforms apps to communication between various parts of my views and and other parts of my apps that are already up and running and need to be notified of changes that have happened.

Given my experience with event aggregators, it felt like a perfect fit for my desire to decouple the views in my medication screen. All I needed was an event aggregation object – one that could have many event subscribers and /  or publishers. Fortunately, backbone provides just the thing I need with it’s event system. All I had to do was create an object that could be shared between my views.

Here’s the entire code listing for my event aggregator object, built with backbone’s event system:

var vent = _.extend({}, Backbone.Events);

That’s it! No, really. That’s all I needed, because backbone already handles events very well. To make use of this, though, I need each of the event publishers and / or subscribers to have a reference to my ‘vent’ object. Once they have a reference, they can either subscribe or publish events as needed.

AddEditView = Backbone.View.extend({
  initialize: function(options){
    _.bindAll(this, "editMedication");
    options.vent.bind("editMedication", this.editMedication);
  },

  editMedication: function(medication){
    this.model = medication;
    this.render();
  }
});

MedicationView = Backbone.View.extend({
  events: {
    "click #edit": "editMedication"
  },

  initialize: function(options){
    this.vent = options.vent;
  },

  editMedication: function(){
    this.vent.trigger("editMedication", this.model);
  }
});

// initialize everything, and tie it all together
// with the event aggregator object: vent

var vent = _.extend({}, Backbone.Events);

var addEditView = new AddEditView({vent: vent});

medicationList.each(function(med){
  new MedicationView({model: med, vent: vent});
});

When the edit icon is clicked, the the vent is triggered with an “editMedication” event, passing the model that needs to be edited directly to any subscribers. The add/edit view has subscribed to the “editMedication” event and receives the medication directly as a parameter. Note that I also used the underscore.js bindAll method to ensure that the editMedication method is executed in the correct context. If you omit this line, the editMedication method will execute with ‘this’ being the event aggregator, not the view.

Now that’s some code that I can really fall in love with! My application functions perfectly – I can click the edit icon and have the correct medication populated into the add/edit view, for editing. Yet my views know nothing about each other. I have completely decoupled them from each other. The only thing the views need to know about is the event aggregator object.

Decoupling The Views

With the event aggregator in place, my views are decoupled from each other. This means that they can change independently of each other. In fact, the MedicationView can go away entirely if I want it to.

I can rename this class, change it’s implementation, delete it and put something else in place or whatever else I want to do. As long as something in my backbone code triggers the “editMedication” event and provides and provides a model as an argument for that event, my add/edit view will respond correctly.

Conversely, I can also modify or remove the add/edit view as needed. As long as some part of my backbone code binds to the “editMedication” event from the event aggregator, and expects to receive a model as the argument for the event handler method, things will continue to work fine.

This set up also lets me have multiple parts of my app listen to the event and respond accordingly. I could easy add other views or other other javascript objects that bind to the “editMedication” event. Each of these binding objects would then be able to respond to the edit click and manipulate the medication model in any way necessary – and all without having to couple any additional views together.

I’ll Take The Event Aggregator, Thanks

The event aggregator is a powerful little pattern. I’m glad I was able to lean on my prior experience with this pattern and introduce it into my backbone code. It made my code significantly easier to work with. Bringing along a simple implementation of a pattern like the event aggregator can help you keep your code clean and maintainable.

The other options I’ve shown do work, of course. There is nothing technically wrong with writing backbone code like that. However, I don’t like having my views coupled together like that. It tends to create a mess when you have multiple nested views and that all need to coordinate with each other.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Backbone, Design Patterns, Javascript, Model-View-Controller, Principles and Patterns. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Matt

    This is exactly what I was looking for! I’ve been struggling with several solutions in my backbone app. In my case, I have a toolbar that applies operations to the “active” view. I didn’t really like the code I had for discovering the active view when a toolbar button is clicked. Event aggregator is much cleaner. Btw, this talk: http://channel9.msdn.com/Events/MIX/MIX11/EXT23  by Andrew Dupont makes mention of using an event broker and is worth watching.

    Thanks!

  • Paul

    Love the simplicity

  • Zerowaste

    verynice! thank you

  • http://www.hanselman.com shanselman

    Rock on. That’s hot. Great writeup.

  • http://www.easyate.com/ Viceroy

    Fine posting would make consistent develop, thanks a ton promote, a build-up with awareness will be to continue to keep knowing, particular attention is definitely the start of huge selection.

  • http://www.astwoodevents.co.uk/ event management companies

    Well written article.I appreciate your writing skills.Its great.You have done a great job by sharing this article with us.I like this article.Keep sharing with us.

  • Dav_twix

    First search in google and I found the solution, thanks, I came from actionscript / Flex developpement and already use this way to let views communicate each other

    Thanks a lot

    • Dav_twix

      Just a question, why not use a Singleton ?

      • Dav_twix

        for exemple : 
        function Notifier() {      _.extend( this, Backbone.Events ) ;        if ( Notifier.caller != Notifier.getInstance )    {          throw new Error(“This object cannot be instanciated”);      }  }    Notifier.instance = null;  Notifier.getInstance = function() {      if (this.instance == null) {          this.instance = new Notifier();      }        return this.instance;  } 

        And to use it : 

        //Listen to ‘menu_changed’ event
        Notifier.getInstance().bind(“menu_changed”, this.menu_changedHandler ) ;

        //dispatch the ‘menu_changed’ event with a argument
        Notifier.getInstance().trigger(‘menu_changed’, { myArgument : ‘test’ } ) ;

        Now, I can instantiate my views in differents scripts with closures without passing the Event aggregator in options

        • http://mutedsolutions.com Derick Bailey

          I like your intent: “Now, I can instantiate my views in differents scripts with closures without passing the Event aggregator in options” but i don’t think the implementation of a singleton is necessary.

          you could just do this:

          Notifier = Backbone.Events.extend({});

          since Notifier is attached to the global javascript object at this point (the same as in your example), its’ available everywhere. there’s really no need to add a getInstance method to it.

          • Dav_twix

            Thanks a lot ! 
            it’s really simple and clear now.

            note : if I try to use your proposition, I got an error : “no method extend in Events Object”, so I use the syntaxe in the Backbone’s documentation and i’s great

            http://documentcloud.github.com/backbone/#Events

            happy coding

          • http://mutedsolutions.com Derick Bailey

            oops – yeah, i make that mistake all the time. :P

  • http://www.facebook.com/profile.php?id=2012258 Patrick Doran

    Totally awesome! I was stuck with a bit of code that I was writing that seemed sloppy or incorrectly implemented and this totally solved my issue.

    • http://mutedsolutions.com Derick Bailey

      glad it helped! :)

  • http://www.facebook.com/profile.php?id=2012258 Patrick Doran

    Totally awesome! I was stuck with a bit of code that I was writing that seemed sloppy or incorrectly implemented and this totally solved my issue.

  • http://www.facebook.com/profile.php?id=694648721 Vikram Malhotra

    Can’t thank you enough for this. Just one question – when triggering an event, you have passed a model to the listeners (editMedication, this.model). I want to know if I can other parameters as well?

    • http://mutedsolutions.com Derick Bailey

      you can pass as many parameters as you want. the first parameter is always the event name, and after that, pass anything / everything:

      vent.trigger(“myEvent”, something, another, whatever, more, stuff, here);

      on the binding end, where you listen to the event, you can either specify all of the parameters manually:

      vent.bind(“myEvent”, function(something, another, whatever, more, stuff, here){…});

      or you can use the build in javascript “arguments” to get them all:

      vent.bind(“myEvent”, function(){
        var something = arguments[0];
        //  etc
      }

  • justin

    Thanks!

  • http://www.slideshare.net/tjercus/ontwikkelen-in-debrowser1 Tjerk

    You can also use jquery  to bind/trigger custom events on the document object. I guess your solution is more testable since it uses a separate event object instead of a global singleton (the document object).

    • http://mutedsolutions.com Derick Bailey

      Yeah, the backbone way i’m showing is a little more testable off-hand… but even that can be solved with jquery. I’ve been reading the “JavaScript Web Applications” book from OReilly Media recently and it shows several examples of using jQuery to create an event aggregator, in a manner that would be easily testable.

      • Tjercus

        Thanks for the book tip! And complements on the article, there are not a lot good sources of information on backbone, especially the practical implementation details in real world situations.

  • Thomas Wieczorek

    Awesome!
    Thank you very much, it made my Backbone code much easier.

  • http://profiles.google.com/dan.meierotto Dan Meierotto

    Just came across this tip, very nice!  How do you deal with context?  Lets say you have two different views both with “ready” events using the same vent. Would you just adopt a naming convention?  e.g. vent.trigger(‘ViewA.Ready’).

    Thanks!

    • Dharani

      I really like this solution and the idea of decoupling vieews. On side note, may be even a less than a side note i dont understand this particular line at all,
      Because of the choice I made, though, having the edit button for a medication instantiate a new add/edit form was not an option for me.

      I guess this led to inject the addeditview instance in the medication view constructor.
      In other words u have exposed 3 ways of programming for the same scenario i understood the first and the third and i guess third is awesome and I dont understand the second at all.

  • http://www.facebook.com/profile.php?id=4302094 Ben Regenspan

    This is very useful, thanks!

    One thing I really like in Backbone is the `events` property of a view – it’s nice to be able to bind in one clear place.

    It’s arguably a total hack and I’m still playing with it, but one way of supporting this still is to extend Backbone.View with a view implementing this custom events function:

            events: function () {            if (!this._events || !this.options.vent) {                return {};            }            var events = $.extend({}, this._events);            for (var key in events) {                if (key.indexOf(‘:’) === 0) {                    delete this._events[key];                    var method = this[events[key]];                    method = _.bind(method, this);                    this.options.vent.bind(key, method);                }            }            return this._events;        }

    And in implementations of the view, supplying an `_events` property rather than `events` property, starting event names that should be passed to the aggregator with “:”

  • Dharani

    sorryf or posting the previous comment in reply to Dan,

    Here it is to Derik,

    I really like this solution and the idea of decoupling vieews. On side note, may be even a less than a side note i dont understand this particular line at all,
    Because of the choice I made, though, having the edit button for a medication instantiate a new add/edit form was not an option for me.

    I guess this led to inject the addeditview instance in the medication view constructor.
    In other words u have exposed 3 ways of programming for the same scenario i understood the first and the third and i guess third is awesome and I dont understand the second at al

  • Anzor B

    This is awesome. Thanks!

  • Josh Gum

    Piece of cake, and so freakin’ handy. 

  • Prasath

    Derick, 
    Thanks for the good article. Do you have the html code for the above one so that I can run & test locally ?

    Thanks

    • http://mutedsolutions.com Derick Bailey

      I don’t – that code was part of a client’s project, so I’m not allowed to share it. There are other examples available, though. The technique I’ve described here has become very popular, recently. 

      • Prasath

        Thanks for your reply. Can you please provide an example of this which is available in online. Thanks for your time.

  • Randy

    Pardon my ignorance on this, but can you give a quick reason as to why an event aggregator is needed?  What’ wrong with one object subscribing to an event, and another one publishing that event?  What advantage do you gain by creating the aggregator?  Trying to see the value add in this…thanks. 

    • http://mutedsolutions.com Derick Bailey

      There’s not necessarily anything wrong with two objects knowing about each other and triggering / subscribing to events directly. It’s impossible to say if it’s a good idea or bad idea without context  - without knowing what the circumstances are, what the objective is, whether or not the objects are already related to each other or not, etc.

      When you have a scenario where two parts of the application need to communicate, but it would cause design problems, tight coupling, spaghetti code messes, or other issues to have them know about each other directly, then an event aggregator is a good option. It’s not the only option, but it is one option.

      • Randy

        Ok, I think I just had a light bulb moment.  The value add is that by extracting out the pub/sub into a separate entity, the triggering object does not need to have a reference to the exact object that is publishing the event, where as it will without that abstraction.  Duh, I was forgetting that that is actually a coupling process in most cases.  We do have a global view element that will always be there, and we publish and subscribe against that object…so in essence that is an event aggregator…but only for certain events that are view related.  Better to have a dedicated event aggregator for ALL events.   

        I guess I was expecting to see some “extra” functionality going on in an aggregator other than just the abstraction and decoupling, but that is probably well worth having it by itself.  It might however, provide a good point for adding code to e.a. object to perform analytics calls against Omniture or Google for each event.  Hmmm…

  • http://www.facebook.com/austin.fatheree Austin Fatheree

    This really helped backbone ‘click’ for me.  Any reason that this isn’t just part of backbone?  It seems like they could implement a Notifier singleton pretty easily.  Then you’d just have to do Backbone.Notifier.bind(‘editMed’, this.editMed);

  • Kaustubh

    Thanks Derick.

    This is second time you’ve helped me in the same day!

  • http://twitter.com/arnislapsa Arnis Lapsa

    Thanks a lot, this is useful stuff. Was right looking for it.

  • Alphatester001

    in a case of collection, where each item of the collection spawn nested view. How would trigger an event on the child view, so only the parent “answer to it” and not all the items of the collection?

  • http://twitter.com/edwardbc Edward Barboza

    Just awesome! thanks for the tips

    I think it’s also worth to mention that you need to unbind the from the events when you no longer needed, otherwise if you’re using that specific event in other place it will trigger the previously set function.

  • littlejim84

    I’m using Require.js (AMD) throughout my application. How is it possible to have a global event dispatcher that can work with modules and not resort to having the event dispatcher on the global namespace? I could pass the event dispatcher object around to every single model/collection/view module, but that seems a little messy. Any other way of doing this?

    • Anonymous

      Here’s how I’m doing it – works like a charm:


      define([   
      'underscore',   
      'backbone'
      ], function(_, Backbone) {   
      var vent = _.extend({}, Backbone.Events);   
      return vent;  
      });

  • Anonymous

    I really like this concept, but I have two questions:

    1. Is it a bad idea to make the event aggregator global or in a namespace like MyNamespace.App.someEvents ?   One of the benefits here is that you can decrease some of the object passing from view to view or model to model.

    2. Do you keep all events for an aggregator within the view context or can a model subscribe to as well?  One main example I see is if you have a collection of Items that have an enabled/disabled state.  There are events, maybe some ajax etc  (triggered through the aggregator) based on a view action.  However, you still want your ItemView to derive it’s active/inactive change from the model Item object’s state.  So should the model itself subscribe?

    • http://mutedsolutions.com Derick Bailey

      1) i recommend doing that. this post is old and reflects my C# way of thinking as I was bringing this stuff in to JavaScript. create a top level namespace where all of your code hangs from, and put the event aggregator on it. 

      2) Any code in your app can publish to or subscribe to the app’s event aggregator. some times it’s best to decouple the views and models from the event aggregator, though. For example, you might want to have an event handler sitting out in the middle of nowhere, and the handler method fires up a view with a model. This is all contextual, though. Do what makes the most sense. Keep in mind the goal of decoupling the various functional areas of your app.

  • Anonymous

    very helpful. thanks so much!!

  • http://twitter.com/plukevdh Lukas

    We decoupled it a little bit further and made it more pubsub-esque:

    https://gist.github.com/1302766

    Kinda mimics Redis in this way now.

  • Dgabriel

    I just started working on my first very complex backbone app, and you have saved me a lot of pain, agony, and irritation here.  Thank you.

  • http://twitter.com/rymodi Ryan Dick

    Isn’t it true that with Backbone 0.5.2, instead of doing:

        _.bindAll(this, “editMedication”);     options.vent.bind(“editMedication”, this.editMedication);

    we can do:

       options.vent.bind(“editMedication”, this.editMedication, this)

    ?

    Got the idea here: http://stackoverflow.com/a/7102686

  • The Random Guy

    Hi I am a newbie and I have implemented the Vent and triggered it when i wanted to edit a particular model inside a view like you did in your example.I am currently faced with an issue ,let me explain in context to your example, how do i show the details of the shops  on clicking the pencil icon for medication based on the medication name and route name,I need to pass the shops model which is not related to the medication model, but i should be able to display the shop model view

    • http://mutedsolutions.com Derick Bailey

      there’s far more detail and code needed to be able to answer this. you should post a question on http://stackoverflow.com where you can post all the detail, including the code in question

      • The Random Guy again

        Hi I have made a backbone.js simple app using asp.net mvc 4 webapi, but i am not sure if i have done it the right way,can you look at my code and suggest me the better approach than I have followed in it.I can give you the source code of it,let me know if you are willing :)

    • The Random Guy

       To be more clear suppose I have one view which shows the list of courses,now on clicking on each and every course i want to show the students enrolled in that course,so inside “click” of the course item view,i called the vent.trigger(“loadStudents”,”model of student…?”) inside the courseitemview,but how do i get the student model for that

  • http://twitter.com/braveheart1723 Braveheart

    Beautiful post Derick – saved me 3 hours work :)

  • Tony Smith

    I am a total newb but I need some help with something very simple: Where is this global variable, yours is “vent”, supposed to go???

    • http://mutedsolutions.com Derick Bailey

      not as a global variable. this is sample code just to show how to define it. you should attach it to a namespace object for your app, or some other object that represents your app and is available to use from your other objects (a module, for example).

  • Suresh Rathord

    Hi,

    I have a question a very basic one though and please correct me if I am wrong. Isn’t vent object a global object ? And isn’t having global variables considered wrong ?

    • Anonymous

      It’s recommended to namespace it to your app:

      We use rt.app.events as a global event handler. Everything under rt.app is an instance variable of some sort so it falls right inline with the structure of our application.

      Other than that as long as you keep global events properly named you should avoid any confusion as to what they do.

      • Suresh Rathord

        @johnml:disqus Meaning that, using a global variables under a namespace is fine. Thanks

  • http://profiles.google.com/eric.bichara Eric Bichara

    Great post thanks

  • http://twitter.com/eljojo josé tomás albornoz

    This post has helped me a lot. Thanks.

  • no one

    G-d forbid you ever link to a demo in any of your posts

  • http://twitter.com/eoinkelly Eoin Kelly

    The Backbone object mixes in events – is there any reason not to use it rather than the custom ‘vent’ object you created here?

    • http://mutedsolutions.com Derick Bailey

      At this point, no. You should just use Backbone directly. When I wrote this almost a year and a half ago, it didn’t provide that. :)

  • http://robcolburn.com Rob Colburn

    FYI: Looks like your syntax highlighter plugins are conflicting, and your getting black-text on black-background.

    Looks like if you disable: /wp-content/plugins/pygment/css/inkpot.css

    Then it will look alright.

  • Jesse

    I’m curious… I am using RequireJS for my Backbone app. I could create an ‘events’ module that extends Backone.Events, much like the example you have here using _.extend(). However, whichever module I choose to use this Event Aggregator with will need to be added as a RequireJS dependency.

    This is not a huge deal, only 2 strings, but considering Backbone is already a required dependency for all my views, models etc., what about just using Backbone.Events as the ‘vent’? That is assuming I am not extending the functionality at all…. This way I wouldn’t need to create any additional dependencies or modules, and just use the Backbone.Events object right out of the box.

    By the way, great site with tons of valuable info! Keep up the good work :)

  • Nelson Omuto

    When I apply this pattern for multiple events, Backbone seems to be triggering the wrong callbacks

  • dias

    awesome

  • b_dubb

    is it required that functions on the separate views have the same name? i find this example confusing. i’m novice to intermediate backbone

  • Kevin Parent

    So is there a reason to not abuse the event aggregator ?

    • http://mutedsolutions.com Derick Bailey

      abuse it enough and you’ll quickly find out :D

      the problems come in when you suddenly realize that you can’t accurately trace through any code in your app for more than one method, because you have to constantly do project wide searches for event names to see what is firing next. this kind of indirection becomes as much of a nightmare as having your code completely tightly coupled together.

      like everything else in software development, it’s all about balance. there are times when two objects will have a natural relationship within the current functionality. just let those objects talk to each other. but there will be times, as mentioned in this post, where things are only tangentially related but don’t have a direct relationship. that’s where it makes sense for event aggregator and other communication patterns.

  • eguneys

    Didn’t understand anything but it works

  • Gonçalo

    thank you