Routing - gregerolsson/booster GitHub Wiki
The built-in Backbone.Router
type fits in nicely in with the other Backbone types
in that you declare routes (often at the top) that map to the various methods in
your particular router. Views work the same; you declare events at the top that
map to functions within the view.
Although symmetrical and fine, if you compare this to how frameworks like Sinatra and Express.js do it you loose the possibility of introducing middleware when setting up routes. In Express.js you can do the following
var app = express.createServer();
function loadExample(req, next) {
req.example = /**
load sample with ID req.params.example from DB asynchronously and
invoke next() when fetched.
**/;
}
app.get('/examples/:example', loadExample, function(req, res) {
res.json(req.example)
});
Here, Express.js will invoke the local loadExample
function before the actual
router function is invoked. Furthermore, loadExample
may be asynchronous and
forward the request to the router function at a later time once the information
from the database has arrived, passing on any information down the middleware
chain via the request object.
Things like these are not possible with the built-in Backbone.Router
and router
functions need to explicitly call helpers like loadExample
and supply callbacks
to handle the result.
In Booster we've added a thin layer on top of Backbone.Router
that mimics the way
Express.js solves routing.
A Single Router
First off, the Backbone support that Booster introduces for routing uses a single
Backbone.Router
instance for handling all of the application routes. From a technical
standpoint using multiple router instances, which is the norm, does not impose any
kind of scoping in the actual routing at run-time -- all the Backbone.Router
instances in an
application register their routes in the same Backbone.history
singleton anyway.
However, routers also trigger events based on the name of the function in the router that is invoked. If you are used to writing code that often binds to router events you will need to be a bit careful when naming the router functions since there is only one router instance.
Note: In our own applications we have never needed to bind to a
router:*
event since the router functions in our case represent the top level functionality of the application. There is nothing "above them" that "controls the controllers/routers". That does not say that other applications do so, and if you depend on this a lot you may be better off using the more traditional way of dealing with routers.
That said, here is what it may look like in your application:
app/assets/javascript/routes/todos.js.booster
var app = require('booster/router'),
todo = require('../models/todo'),
views = require('../views/todo);
function loadTodo(req, next) {
req.todo = new todo.Model({id: req.params.id});
todo.fetch({ success: next });
}
app.route('/todos:id', loadTodo, function show(req) {
var view = new views.Show({model: req.todo});
/** Insert view in DOM somewhere **/
});
app.route('/todos:id/edit', loadTodo, function edit(req) {
var view = new views.Edit({model: req.todo});
/** Insert view in DOM somewhere **/
});
It is quite likely that you will have several files under routes/
that
deal with a specific set of routes that are related to eachother. Since
each file is a CommonJS-like module they can have their own set of private
variables or helper functions like loadTodo
.
Should you need to access the underlying Backbone.Router
instance for some reason,
it is exported along with the route
function from the booster/router
module:
app.router.bind('route:show', function() {
console.log('The `show` route was invoked');
});
The name of the last middleware function in a call to route
becomes the
name of the route, in case you want to listen for events. In the example
above, these are named show
and edit
, respectively. If you don't care
about these events you can use anonymous functions.
Again, note that all routes are stored in the same Backbone.Router
instance
behind the scenes and if you use named functions that you want to listen for events
on, make sure they are unique for the whole application.