Assuming you’ve used Backbone prior to reading this book — this entire book assumes you have, even if I myself wouldn’t — you should be familiar with Backbone.Router
. Routers are great things that allow single page applications to have multiple URLs as if they were applications spanning multiple pages. This grants users the ability to use the Back and Forward buttons in the browser to navigate around apparent pages of your application that they have seen before, or even bookmark a particular place in your application.
Every good application with more than one screen should take advantage of a router to provide these expected pieces of the user experience. So, let’s get started using Backbone.Router
, right? Nah, I have a better idea: let’s use Marionette.AppRouter
.
The AppRouter
is exactly the same as Backbone’s Router
— in fact it extends Router
and you can just replace all instances of Router
with AppRouter
and your application will continue to work exactly the same way — except that it has an additional feature that allows you to use the router in a better way. In chapter 1, I mentioned that people often used their router as the central application namespace object, which is outside the responsibilities of a router. Well, apparently, the router comes under lots of misuse — though this time it isn’t our fault.
Backbone’s Router
was designed to handle the full processing of all of the routes within itself, but if you’ve ever looked at a router on the server, it is little more than configuration used to delegate actions to controllers based on the URL. AppRouter
is set up more like a back-end router: it allows you to configure it with a controller, and then respond to a URL change by calling a method on the controller rather than on the router itself.
This keeps the concerns of determining a URL change and parsing it separate from controlling the application based on those changes. In other words: let the router route and the controller control.
Derick has an interesting example in a blog post1 that demonstrates why he created the AppRouter
. When he was using Backbone’s Router
, his code looked similar to this:
var MyRouter = Backbone.Router.extend({
routes: {
"": "main",
"/category/:catid": "category",
"/item/:itemid": "item"
},
main: function() {
App.showMain();
},
category: function(catid) {
App.showCategory(catid);
},
item: function(itemid) {
App.showItem(itemid);
}
});
This is not the same example he gave, but it’s very similar — it’s really just a simplified and generalized version of his code. With some inspiration from Marionette users’ feedback, he decided that instead of just passing requests through to the application manually, he would create a means for himself to add the application as a controller that the router calls methods on directly. Thus AppRouter
was born. And hence the name “AppRouter” instead of something like “ControllerRouter”. Obviously, you don’t need to use your application object as the controller, but if you’re using the application object in the same way as Derick was, it’s not a bad choice.
I prefer to use a separate object as the controller, but as long as you’re removing the responsibility of controlling models and views from the router, you’re taking good steps toward better structure.
So, how do we add that controller to an AppRouter
? There are two ways to do it: when extending the AppRouter
, or when instantiating one. No matter which method you choose to use, you do it by specifying a controller
property that points to the object you want to use as the controller.
var MyRouter = Marionette.AppRouter.extend({
controller: App, // when extending
...
});
router = new MyRouter({controller: App}); // when initializing
Note that no AppRouter
can use more than one controller object.
Of course, it doesn’t do us any good to assign a controller to the router if we don’t know how to make the router use the controller. Backbone’s Router
uses the routes
property to specify the routes and map methods to them, which you can also do with AppRouter
:
var MyRouter = Marionette.AppRouter({
routes: {
"": "index",
...
},
index: function() {
...
},
...
});
This is why you can just replace your old routers with AppRouter
and not have any changes. But if you want to map routes to methods on the controller, you use the appRoutes
property:
var MyRouter = Marionette.AppRouter.extend({
controller: App,
appRoutes: {
"": "index",
...
},
...
});
The syntax is exactly the same. The only difference is that the method name refers to a method on the controller, rather than a method on the router. You can use both routes
and appRoutes
in the same router, so the routes specified in routes
will use methods on the router, while the routes specified in appRoutes
will use methods on the controller. It’s important to note that if you have a route that appears in both routes
and appRoutes
, the appRoutes
route will take precedence and the one in routes
will never be called.
If you’d like to add routes dynamically after the router is initialized, you can use the appRoute
method. It works in exactly the same way as the route
method on Backbone’s Router
s, except of course that it adds a route that uses the method on the controller rather than on the router. When using appRoute
(or just route
), you need to pass in two arguments: the route as a string; and the name of the method on the controller that you want to call when the URL matches that route. This is also sent in a string.
router.appRoute('/list/', 'showList');
There are alternative syntaxes for route
and appRoute
where you can specify a callback function instead of using a method on the router or controller, but I won’t show you those. The use cases are few and far between and aren’t very good practice. If you want to learn more about that syntax, take a look at Backbone’s documention for the route
method2.
If you’re using a router in your web applications, then Marionette’s AppRouter
is a very good choice. Even if you’re already using Backbone’s Router
, you can just switch them all up for AppRouter
without any issues, and you’ll be set for the future when or if your application grows to the point where you need better organization and structure. If you’re just starting your application, there’s no good reason not to use the AppRouter
over Router
.
It may only be a small step up from the Router
, but it can have a noticeable impact on the way you write your code and keep your routers clean.