The Marionette AppRouter
extends the Backbone.Router
to
make it easier to construct a large number of routes for your app. This is
particularly useful if you want to build a large single-page app while keeping
your router's core logic readable.
The Marionette AppRouter is typically used to set up your app when the user loads a specific endpoint directly. Typically, in a single-page app, users will expect to be able to easily navigate back to a section of the application by bookmarking a URL and loading it directly. Let's look at a concrete example:
var Mn = require('backbone.marionette');
var EmailController = {
showEmail: function(email) {
// Look up the email and display it in our main layout
}
};
var EmailRouter = Mn.AppRouter.extend({
controller: EmailController,
appRoutes: {
'emails/:email': 'showEmail'
}
});
Assuming our application is served from the root, whenever the user accesses
http://ourapplication.com/#emails/email-subject-line-123
, the method
showEmail
will be called with email-subject-line-123
as its argument. This
will be covered in more detail below.
The AppRouter uses the appRoutes
option to define how we respond to routes
being accessed. To define routes, set the route as your key and the method to
call as a string referencing a method on your controller. For more information
on route definitions, see the [Backbone documentation][#backbone-routes].
The major difference between appRoutes
and routes
is that we provide
callbacks on a controller instead of directly on the router itself. This allows
you to define a simpler router and keep your controller logic closer to the
modules it interacts directly with:
var Mn = require('backbone.marionette');
var EmailController = require('./emails/controller/email');
var MyRouter = Mn.AppRouter.extend({
controller: EmailController,
// "someMethod" must exist at controller.someMethod
appRoutes: {
'email': 'listEmails',
'email/:email': 'showEmail'
}
});
As the AppRouter
extends Backbone.Router
, you can also define a routes
attribute whose callbacks must be present on the AppRouter
:
var Mn = require('backbone.marionette');
var MyRouter = Mn.AppRouter.extend({
routes: {
'email/:email': 'showEmail'
},
showEmail: function(email) {
// show the email
}
})
See the Backbone documentation for more information about
defining routes
.
If you want more control when managing your routes, you can define your routes on router instantiation:
var Mn = require('backbone.marionette');
var EmailController = require('./emails/controllers/email');
var MyRouter = new Mn.AppRouter({
controller: EmailController,
appRoutes: {
'email/': 'listEmails',
'email/:email': 'showEmail'
}
});
In addition to setting the appRoutes
for an AppRouter, you can add app routes
at runtime, to an instance of a router. This is done with the appRoute()
method call. It works the same as the built-in router.route()
call from
Backbone's Router, but has all the same semantics and behavior of the
appRoutes
configuration.
var Mn = require('backbone.marionette');
var MyRouter = Mn.AppRouter.extend({});
var router = new MyRouter();
router.appRoute("/foo", "fooThat");
Also you can specify a controller with the multiple routes at runtime with the
processAppRoutes
method. This will preserve the existing controller as well:
var Mn = require('backbone.marionette');
var MyRouter = Mn.AppRouter.extend({});
var router = new MyRouter();
router.processAppRoutes(myController, {
"foo": "doFoo",
"bar/:id": "doBar"
});
App routers can only use one controller
object. You can either specify this
directly in the router definition:
var Mn = require('backbone.marionette');
var someController = {
someMethod: function(){ /*...*/ }
};
Mn.AppRouter.extend({
controller: someController
});
The object that is used as the controller
has no requirements, other than it
will contain the methods that you specified in the appRoutes
.
A controller can also be an instance of
Marionette.Object
- this is useful for cases where
you want to access the helper tools of the Object API and pass through
information on instantiation.
Marionette allows you to run multiple AppRouters in a single application. It's recommended that you break your routing into multiple sections, each with its own router and/or controller setting up the views for their own components. This will make it much easier to find and manage your route-handling logic as your application grows in complexity.
The Backbone History API monitors the browser's location bar and triggers route changes on your app routers. It also provides a set of methods to change the contents of the location bar manually when you want to expose functionality to your user via a URL:
var Bb = require('backbone');
var Mn = require('backbone.marionette');
var EmailView = require('./email/views/email');
var EmailList = Mn.View.extend({
regions: {
layout: '.layout-hook'
},
showEmail: function(model) {
this.showChildView('layout', new EmailView({model: model}));
Bb.history.navigate('email/' + model.id);
}
});
As stated in the Backbone documentation, navigate
takes an options
argument
that lets you trigger
on route change. We recommend against using this as it
tends to cause side-effects like making it hard to ensure the route is only
navigated to once, or unintentionally firing different route changes.
When the user navigates to a new route in your application that matches a route
in your AppRouter
, the route
event will be fired. Listening to this will let
you perform extra custom behavior:
var Mn = require('backbone.marionette');
var Controller = require('./email/controller');
var MyRouter = Mn.AppRouter.extend({
controller: Controller,
appRoutes: {
'emails/:email': 'showEmail'
},
onRoute: function(name, path, args) {
console.log('User navigated to ' + path);
}
});
This event handler takes three arguments:
name
- Name of the routepath
- Path that triggered this eventargs
- Arguments passed into the route