I needed this for a recent project, I plan to release this code as open source at some point, but you can do something like this:
Create a global router to handle all routing:
App.GlobalRouter = Backbone.Router.extend({
initialize: function(){
this._routes = {};
},
registerRoute: function(route, rootRoute){
var rootName;
if(rootRoute) {
route = rootRoute + '/' + route;
rootName = this.registerRoute(rootRoute);
}
if(!_.isRegExp(route))
route = this._routeToRegExp(route);
var name = this._routes[route] ? this._routes[route] : _.uniqueId('r');
this._routes[route] = name;
this.route(route, name, function(){});
if(rootName) {
this.on('route:'+name, function(){
var args = slice(arguments);
this.trigger.apply(this, ['route:' + rootName].concat(args));
}.bind(this));
}
return name;
}
});
Then create a single one:
App.globalRouter = new App.GlobalRouter();
Then create a modified router to extend all your routers from:
App.Router = Backbone.Router.extend({
constructor: function (options){
options = options || {};
if(options.root) this.root = options.root;
this.globalRouter = App.globalRouter;
Backbone.Router.apply(this, [options]);
},
route: function(route, name, callback, root){
if(!App.globalRouter) return false;
// If callback is root param
if(callback && !_.isFunction(callback)) {
root = callback;
callback = null;
}
// If no name is callback param.
if(_.isFunction(name)) {
callback = name;
name = '';
}
if(!callback)
callback = this[name];
var router = this;
var roots = root || this.root;
if(roots && !_.isArray(roots)) roots = [roots];
if(roots) {
_.each(roots, function(root){
var globalName = App.globalRouter.registerRoute(route, root);
router.listenTo(App.globalRouter, 'route:'+globalName, function(){
var args = slice(arguments);
var callbackArgs = args.slice(callback && -callback.length || 0);
callback && callback.apply(router, callbackArgs);
router.trigger.apply(router, ['route:' + name].concat(callbackArgs));
router.trigger('route', name, callbackArgs);
});
});
} else {
var globalName = App.globalRouter.registerRoute(route);
router.listenTo(App.globalRouter, 'route:'+globalName, function(){
var args = slice(arguments);
var callbackArgs = args.slice(callback && -callback.length || 0);
callback && callback.apply(router, callbackArgs);
router.trigger.apply(router, ['route:'+name].concat(callbackArgs));
router.trigger('route', name, callbackArgs);
});
}
return this;
}
});
From here you can create as many routers that are required and register them on the same route, also you can create a router that has route routes to listen on, so in your case you would have probably 2 or 3 routers, here is an example of what you could do:
var defaultRouter = App.Router.extend({
routes: {
'dashboard': 'showDashboard',
'game/:id': 'showGame'
},
showDashboard: function() {},
showGame: function(id) {},
});
var profilerRouter = App.Router.extend({
root: [
'dashboard',
'game/:id'
],
routes: {'profile', 'showProfile'},
showProfile: function(){//Show lightbox}
});
This will listen for /dashboard
or /game/:id
and call that funciton on defaultRouter
that is listening. Then if the /profile
is on the end of the url for either of the routes is will catch that and run the showProfile
function on the profileRouter.
NOTE: I've quickly modified the code take from my project to change some of the name/namespace issues, so you might need to check that I haven't missed anything, but the code should be right otherwise
Updated Example:
- If the user navigates to
/game/:id
it will call the defaultRouter > showGame
with param :id
.
- If the user navigates to
/game/:id/profile
it will call the defaultRouter > showGame
with param :id
. It will also call profileRouter > showProfile
but with no params (ie. it doesn't send the :id from the /game/:id root).