This library wraps the Backbone.Router
to simplify its use and bring new functionalities
Its structure and API is inspired by routers in the Node.js frameworks: Meteor and ExpressJS.
Added functionalities compared to the Backbone.Router
are:
- Multiple controllers for the same path
- Before and After triggers
- Trigger caching
- Aliasing
- "Secured" routes
You can install the library via bower:
bower install backbone-router
The project has been renamed from marionette-router to backbone-router, because the Backbone.Marionette
dependency has been removed. It now overrides the Backbone.Router
namespace for simplicity.
The dependencies left are:
- Backbone 1.1.4
- Underscore >= 1.4.4
Declaring routes goes through executing a simple method: Backbone.Router.map();
This method takes a function as its only parameter which will be executed in the router's context to access the internal API easily. A route consists of a unique name and an object to describe the route's action.
Let's just jump right in with an example:
// Create a marionette app instance
var App = new Backbone.Marionette.Application();
// Start route declarations
Backbone.Router.map(function() {
// Declare a route named 'home'
this.route("home", {
// The url to which the route will respond
"path": "/",
// Method to be executed when the given path is intercepted
"action": function() {
// Do something fantastic \o/
}
});
// Declare other routes...
});
// Wait for document ready event
$(function() {
// Start the marionette app
App.start();
// Start the router passing the marionette app instance
Backbone.Router.start(App);
});
The router has to be started via the start
method.
Parameters:
- App (Mixed) - Can be an instance of
Backbone.Marionette.Application
or a copy ofBackbone.Events
. Will be used to execute triggers declared in routes. - Options (Object) - Override default router configuration
If given a Marionette app instance the router will use the vent
global event aggregator to distribute route triggers.
Building on the previous script, here is an example:
// Create app
var App = new Backbone.Marionette.Application();
// Define some routes ...
// Start the marionette app
App.start();
// Start the router passing the marionette app instance and an options object
Backbone.Router.start(App, {
// Root url for all routes, passed to Backbone.history
"root": "/admin",
// Activate html5 pushState or not, true by default
"pushState": false,
// Whether the user is currently logged in or not
"authed": false,
// If not logged in, redirect the user to a route named "login" (if it exists)
"redirectToLogin": false,
// Print out routing debug information to the console
"debug": true
});
Or passing a Backbone.Events
copy:
// Copy Backbone.Events
var dispatcher = _.extend({}, Backbone.Events);
// Start router
Backbone.Router.start(dispatcher);
The dispatcher can also be overridden before the router is started in this way:
Backbone.Router.dispatcher = _.extend({}, Backbone.Events);
To redirect the user to a certain route when, for example, he clicks a link simply use the go
method.
Backbone.Router.go("home");
Parameters
- name (Mixed): The route name to execute or an object describing the route.
- args (Mixed): Array of arguments, can also be a function's
arguments
object. - options (Object): Passed to the Backbone.Router navigate method. Defaults to
{ "trigger": true, "replace": false }
Let's define a route that takes a parameter:
Backbone.Router.map(function() {
// Declare a user profile page
this.route("user_profile", {
"path": "/user/:id",
"action": function(userId) {
// Render user profile page
}
});
})
Considering the current page contains a link like this:
<a href="/user/42" class="profile" data-id="42">Your profile!</a>
We could write a script (using jquery) to redirect the user like so:
// Intercept the user click
$("a.profile").click(function(e) {
e.preventDefault();
var userId = $(this).attr("data-id");
// Redirecting to route named "user_profile" passing an id
Backbone.Router.go("user_profile", [userId]);
});
As the first parameter to the go
method can be an object, we could also write the previous script in this manner:
// Intercept the user click
$("a.profile").click(function(e) {
e.preventDefault();
// Redirecting to route using the path defined in the href attribute
Backbone.Router.go({ "path": this.href });
});
The path
and action
parameters are the base of a route. But a few more parameters exist to extend the control of the route.
// Definition object for a route named 'user_edit'
{
// Path with an 'id' parameter
"path": "/user/:id/edit",
// Route will only be executed if the user is logged in
"authed": true,
// Execute triggers before the 'action' controller
"before": [
{ "name": "core:display", "cache": true },
"users:display"
],
// Main controller for the route
"action": function(userId) {
// Render a user edit form
},
// Execute triggers after the 'action' controller
"after": [
"core:post_triggers"
],
// Executed when user is routed away from this route
"close": function() {
// Return false to cancel the routing
return confirm("Are you sure you want to leave this page?");
}
}
A route named 404 can be declared to catch all non-existent routes. In the same way a route can be named 403 to catch accessing restricted routes.
Backbone.Router.map(function() {
// 404 controller
this.route("404", {
"action": function(path) {
// Couldn't find what you're looking for =/
}
});
// 403 controller
this.route("403", {
"action": function(path) {
// Sorry you can't access this content =(
}
});
});
For convenience, the action methods will receive the current window.location.pathname
as the first argument.
The 404 controller will also be executed when a non-existent route is called with the go
method.
The 403 controller will only be executed if the redirectToLogin
option is set to false
.
To distribute the triggers declared in the before
and after
parameters the Backbone.Router
uses the Marionette
global event aggregator: App.vent
This parameter can be overridden using any Backbone.Events
instance.
var App = new Backbone.Marionette.Application();
// Create a custom event aggregator
var myDispatcher = _.extend({}, Backbone.Events);
// Pass the custom object to the Router
Backbone.Router.dispatcher = myDispatcher;
App.start();
Backbone.Router.start(App);
Triggers can be declared in different ways.
They can be a simple String
for the simple ones:
{
// ...
"before": [
"core",
"module",
"submodule"
],
// ...
}
They can also be declared as an object with different parameters:
{
// ...
"before": [
{ "name": "core", "cache": true },
{ "name": "module", args: [foo, bar] },
"submodule"
],
// ...
}
Most importantly: Each declared route becomes a trigger itself so that routes can build on each other.
Each route can receive an authed
boolean parameter to declare if the route should be interpreted when the user is logged in or not.
Backbone.Router.map(function() {
// Declare secure route
this.route("secure_route", {
"path": "/admin/users",
"authed": true,
"action": function() {
// Display list of users
}
});
});
To make a route be interpreted in both cases (i.e. when the user is logged in or logged out),
simply leave out the authed
parameter in the route declaration.
Important
Only the server has the authority to tell if a connected client is a logged in user or not. So for this system to actually work, the server has to print out a small piece of JavaScript to tell the router the current client's state:
<script type="text/javascript" src="backbone.router.js"></script>
<script type="text/javascript">
window.logged_in = <?php if ($_SESSION['logged_in']): ?>true<?php else: ?>false<?php endif; ?>;
$(funtion() {
// Starting the marionette app
App.start();
// Starting the router telling it if the user is logged in or not
Backbone.Router.start(App, {
"authed": window.logged_in
});
});
</script>
An implementation example index.php
file is available in the repository. To run it create an apache vhost or use any web server you like.
So that client-side routing can work, every request sent to the server must be answered with the same code,
therefore an .htaccess
file activating mod_rewrite and redirecting all requests to the index.php
file is also available in the repository.