2
votes

I have an application that I want to create using Backbone Marionette. Basically, it's UI structure is very simple. There are:

  • Two top level regions, one for the menu, the other for the content
  • The menu region shows a menu view
  • The content region shows two views next to each other
    • The left one is for a sub-navigation
    • The right one is for the actual content

As an example, the menu might switch between two parts of the application, such as "mail", "calendar" and "contacts". Each of those parts has its own sub-navigation which shall be shown in sub-navigation view, and each part has a default view. E.g., in the "mail" part this may be the "inbox" view, in the "calendar" part this may be the "month" view.

So, generally, we have a nested navigation.

How do I implement this using routers?

My idea is to have an application-level router that just provides routes for the parts such as #mail and #calendar.

The sub-navigation views then should have their own routers. So, e.g., the mail sub-navigation view could have a router for inbox and sent.

In the end I want to have a route such as #mail/inbox, but the first part should be handled by the top-level router, the second part should be handled by the sub-level router.

My question is whether I can nest routers in a way that the sub-level routers do not need to know the URL prefix such as "mail", and there is a cascading routing happening. Is this possible?

Or is this approach completely wrong?

1

1 Answers

2
votes

I don't know if Marionette has a solution for this, but there's a plugin called Backbone.subroute, which provides the kind of functionality you're looking for.

Separating sub-routes in this way is a legitimate pattern, and I've used it previously. However, I've since come to the conclusion that having each section's "main" route in the main application router was not the way to go, because it separates concerns that should live together. Instead I've started implementing a common base class for routers, which provides for a "root" URL:

var BaseRouter = Backbone.Router.extend({

    //Root path for all routes defined by this router. Override this in a deriving
    //class for keeping route table DRY.
    urlRoot: undefined,

    //override the route method to prefix the route URL
    route: function(route, name, callback) {
      if(this.urlRoot) {
        route = (route === '' ? this.urlRoot : this.urlRoot + "/" + route);
      }

      //define route
      Backbone.Router.prototype.route.call(this, route, name, callback);

      //also support URLs with trailing slashes
      Backbone.Router.prototype.route.call(this, route + "/", name, callback);
    }
});

Usage:

var CalendarRouter = BaseRouter.extend({
    //all routes will be relative to "calendar"
    urlRoot:"calendar",
    routes: {
        //...
    }
}): 

Granted, for my case this makes sense also because I define other common router tasks in the base class. Just to avoid typing the extra "calendar" prefix for each route url doesn't really warrant it.

Edit: Just to clarify, the Backbone.SubRoute plugin doesn't force you to define the root route in the main router, you can use it in the same way as the base class I suggested. But if all you want is a route prefix, then the base class is a lighter, simpler solution.