Reducing Backbone Routers To Nothing More Than Configuration

I received an email from someone who was looking through my BBCloneMail and Backbone.Marionette source code. One of the comments they made talked about how my router callback methods were nothing more than pass-throughs, calling out to another object. (I think this is one of the points that Tim Branyen was making in the comments of my previous post, too).

Here’s an example of the code that the email was referring to:

  MyRouter = Backbone.Router.extend({
    routes: {
      "": "mail",
      "inbox": "mail",
      "inbox/categories/:category": "mailCategory",
      "inbox/:id": "mailMessage"
    },

    mail: function(){
      BBCloneMail.MailApp.show();
    },

    mailCategory: function(category){
      BBCloneMail.MailApp.showCategory(category);
    },

    mailMessage: function(messageId){
      BBCloneMail.MailApp.showMessage(messageId);
    }
  });

The emailer suggested that I factor out the boilerplate code and set up some custom functionality to go straight from the route definition to a method call on another object, In other words, instead of having to implement a callback method on the router itself, having the callback method be on another object.

Removing The Callback Functions

I like the idea so I hacked together a working version directly in to the BBCloneMail application. The result is that my router went from the code above, to this:

  MyRouter = BBCloneMail.AppRouter.extend({
    routes: {
      "": "showInbox",
      "inbox": "showInbox",
      "inbox/categories/:category": "showCategory",
      "inbox/:id": "showMessage"
    }
  });

Now my router is nothing more than configuration! There are no callback methods directly in the router anymore. The callbacks that I defined are located on another object, instead.

To make this work, my “AppRouter” expects to receive an “app” object when it’s instantiated. This is the object that contains the callback methods I specified.

new MyRouter({
  app: BBCloneMail.MailApp
});

All of the parameters that you define in the route are forwarded to your app object’s callback method. So, when the empty (“”) route fires, the “showInbox” method on the “BBCloneMail.MailApp” object will be called with no parameters. And when “inbox/:id” fires, the “showMessage” method on “BBCloneMail.MailApp” will receive a single parameter with the id from the route.

The AppRouter Code

Here’s the code that I put in to my AppRouter, to make this work:

  Routing.AppRouter = Backbone.Router.extend({

    constructor: function(options){
      Backbone.Router.prototype.constructor.call(this, options);

      if (this.appRoutes){
        this.processAppRoutes(options.app, this.appRoutes);
      }
    },

    processAppRoutes: function(app, appRoutes){
      var method, methodName;
      var route, routesLength;
      var routes = [];
      var router = this;

      for(route in appRoutes){
        routes.unshift([route, appRoutes[route]]);
      }

      routesLength = routes.length;
      for (var i = 0; i < routesLength; i++){

        route = routes[i][0];
        methodName = routes[i][1];
        method = app[methodName];
        router.route(route, methodName, method);

      }
    }

  });

This code doesn’t preclude you from using the standard “routes” and callbacks, either. You can mix and match the standard routes and the appRoutes however you want. The routes defined in appRoutes get processed after the standard routes, though. This means it’s possible for an appRoute to override a normal route.

Still Needs Work

You can see it live in my BBCloneMail app right now. It may not stay there for long, though, as I’m constantly changing that code. Also – I haven’t thoroughly tested this so there may be some browser compatibility issues that I haven’t run into yet… or some other issues that I don’t know about. I’m going to keep playing with this idea and once I get it to something I really like, I’ll re-write it (test-first of course) into Backbone.Marionette.

I’m also looking for feedback on this. I realize there are some limitations to this, and there are probably some dumb thing I’ve done, too. If anyone has any suggestions for how to improve this, I’d love to hear it.


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, Javascript, Marionette. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://dannydo.es/ Dan (Danny) Fellars

    Hey Derick, I was going through your BBMailApp code and saw this boiler plate code and wondered why it wasn’t part of Marionette itself.  Good to hear that is your plan.  That would be very useful.

  • http://twitter.com/colin_jack Colin Jack

    Looks like something I would definitely like to try, keep up the good work with
    these excellent BB posts.

  • http://www.facebook.com/zach.curtis.961 Zach Curtis

    Overriding the router ctor was the ticket for me! I messed around with overriding initialize WAY too long. Thanks so much.

  • _B@DR_

    hi, thx for this post :) i would like know if i can passed object to my router into url :

    Ex : “mymethodName/:object”: “mymethodName”

    and method implementation is :

    mymethodName : function (object){
    //do somthing with my object
    }

    Thx :)

  • Ron Boles

    Interesting article Derick…but I notice that you have hard-coded the folder-name (inbox) into your routes. I believe a more correct design would be to abstract the routes something like this:-
    “list/:folder” : “listfolder”, // eg. list/:inbox -or- list/:sent
    “list/:folder/:from” : “listfolder”, // eg. list/:saved/:fred
    I have worked for employers who would fire you for hard-coding specific data items into routes! Please show us backbone beginners how to implement the above AND it will work with back/forward/reload buttons.
    Thanks and Cheers

  • Andy Novocin

    I’m a bit late to the party but I think that there is a typo here. The typo added some complexity to figuring this technique out at home.

    If I understand correctly (rolled back the bbclonemail git repo to Jan. 2012) routes which are inside the “appRoutes” attribute get processed by app.whatever methods and things inside the “routes” attribute get processed normally.

    In line 2 of the second snippet “routes” should probably be “appRoutes”, so that your code at line 6 of the fourth snippet has something to work with.