Routing Backbone with style \o/
Backbone.Highway
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 distributed using an event aggregator
- Simple trigger cache managing
- Aliasing between routes
- "Secured" routes
- Close action (similar to onbeforeunload)
Installation
You can install the library via npm:
npm install backbone-highway
Or bower:
bower install backbone-highway
Dependencies and structure
The dependencies are:
- Backbone >= 1.1.4
- Underscore >= 1.4.4
Until now the library was overriding the Backbone.Router
namespace. I now understand that this was a huge mistake as it was breaking the dependencies of other Backbone libraries by replacing the core API. Thus the new name Backbone.Highway
General use
Declaring routes goes through executing a simple method: Backbone.Highway.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:
var App = {
events: _.extend({}, Backbone.Events)
};
Backbone.Highway.map(function () {
this.route('home', {
path: '/',
action: function () {
}
});
});
$(function () {
Backbone.Highway.start({
dispatcher: App.events
});
});
Start routing
The router has to be started via the start
method.
It receives an options
object containing at least a dispatcher
Parameters:
- Options (Object) - Override default router configuration
These are the default options :
var defaultOptions = {
pushState: true,
root: '',
hashChange: true,
silent: false,
dispatcher: null,
authenticated: false,
redirectToLogin: false,
routes: {
login: 'login',
error404: '404',
error403: '403'
}
debug: false,
log: function () {
if (this.debug && window.console && window.console.log) {
window.console.log.apply(window.console, arguments);
}
}
};
Example of overriding the default options :
var App = {
events: _.extend({}, Backbone.Events)
};
Backbone.Highgway.start({
dispatcher: App.events,
root: '/admin',
pushState: false,
authenticated: true,
redirectToLogin: true,
debug: true
});
Router go!
To redirect the user to a certain route when, for example, he clicks a link simply use the go
method.
Backbone.Highway.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.Highway.map(function () {
this.route('user.profile', {
path: '/user/:id',
action: function(userId) {
}
});
})
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:
$('a.profile').click(function (e) {
e.preventDefault();
var userId = $(this).attr('data-id');
Backbone.Highway.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:
$('a.profile').click(function (e) {
e.preventDefault();
Backbone.Highway.go({
path: $(this).attr('href')
});
});
Route declaration parameters
The path
and action
parameters are the base of a route. But a few more parameters exist to extend the control of the route.
{
path: '/user/:id/edit',
authenticated: true,
before: [
{ name: 'core.display', cache: true },
'users:display'
],
action: function (userId) {
},
after: [
'core.postTriggers'
],
close: function () {
return confirm('Are you sure you want to leave this page?');
}
}
Catching client-side 404 and 403
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.Highway.map(function () {
this.route('404', {
action: function (path) {
}
});
this.route('403', {
action: function (path) {
}
});
});
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
.
Events distribution (Triggers)
To distribute the triggers declared in the before
and after
parameters Backbone.Highway
uses an instance of any Backbone like event aggregator. It has only been tested with Backbone.Events
and Backbone.Wreqr
.
Backbone.Highway
only tests if the passed object has a trigger
method. So it could be anything you like.
var App = {
events: _.extend({}, Backbone.Events)
};
Backbone.Highway.start({
dispatcher: App.events
});
Trigger declaration
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.
Secured routes
Each route can receive an authenticated
boolean parameter to declare if the route should be interpreted when the user is logged in or not.
Backbone.Highway.map(function() {
this.route('admin.users', {
path: '/admin/users',
authenticated: true,
action: function () {
}
});
});
To make a route be interpreted in both cases (i.e. when the user is logged in or logged out),
simply leave out the authenticated
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.highway.js"></script>
<script type="text/javascript">
window.LOGGED_IN = <?php if ($_SESSION['logged_in']): ?>true<?php else: ?>false<?php endif; ?>;
$(funtion() {
Backbone.Highway.start(App, {
authenticated: window.LOGGED_IN
});
});
</script>
Demo / Example
A demo is available in the demo
folder.
Use npm and bower to install dependencies and grunt to launch the demo server.
~/backbone-highway$ npm install && bower install
~/backbone-highway$ grunt serve
License
The MIT License (MIT)
Copyright (c) 2015 d4f
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.