# Backbone Router

I am thinking of writing some online notes/book. Amongst other things, I want to use Backbone to show different sections within a chapter as separate views. In other words, I want each chapter to behave like a single page application; different chapters will behave as separate single page applications. Within a chapter, as the user navigates from one section to another, I don't want the page to reload and am thinking of using the Backbone router to show these section views.

Please take a quick look at the code below and let me know if you see anything problematic with how I am using Backbone routing to render views.

The code works, but I want to know if I am doing anything inefficiently and if there are any "good practice" principles that I am violating. For example, I create new view instance every time the route changes. Is there a way to do this better?

(function() {
var bApp = {
model: {},
view: {},
collection: {},
router: {}
};

window.bApp = bApp;

bApp.view.section = Backbone.View.extend({
el: 'div#chapter2',

template: nunjucks.render('./client/views/client-templates/ch2_sec2.html'),

render: function() {
this.$el.html(this.template); return this; } }); bApp.router = Backbone.Router.extend({ routes: { '': 'showroute_ch2sec1', 'ch2sec1': 'showroute_ch2sec1', 'ch2sec2': 'showroute_ch2sec2' }, showroute_ch2sec1: function() { section1 = new bApp.view.section; section1.template = nunjucks.render('./client/views/client-templates/ch2_sec1.html'); section1.render(); }, showroute_ch2sec2: function() { section2 = new bApp.view.section; section2.template = nunjucks.render('./client/views/client-templates/ch2_sec2.html'); section2.render(); } }); var r = new bApp.router; Backbone.history.start(); })();  • Do you have a <div id="chapter2"> in the DOM? Are you sure you want to render both views into the same element? – mu is too short Oct 27 '13 at 21:43 • Sorry for the wrong title. The code works. I had chosen the title earlier when it was not working and then I got it to work. When I wrote the question above I forgot to change the title. My question is that since I am so new to Backbone I wanted to know whether there are any clearly "bad approaches" that I am using. The code, however, works. – Curious2learn Oct 27 '13 at 22:01 • @SeanVieira I was not aware of that site. Can you move the question there, or do I have to do it myself? Would appreciate instructions on how to do so, if I am supposed to do move it to that site. – Curious2learn Oct 28 '13 at 12:39 • @Curious2learn - unfortunately, I do not have the power to do this myself - I'll flag it for moderator attention and see if they can do it. – Sean Vieira Oct 28 '13 at 14:35 • @SeanVieira: I flagged it too. I'll try to keep an eye on it and give it a review once it has moved. – mu is too short Oct 28 '13 at 19:16 ## 1 Answer A few minor tweaks could help you out: • Add a close method to all of your views, which cleans up your DOM, and unbinds any bound events (eg, with this.stopListening()). This will prevent 'zombie events' -- events bound to views which are no longer rendered. • Move the logic for switching pages to a high-level application contoller. This will keep your router from getting too logic-heavy, and will allow the application controller to handle view creation and cleanup. Here's how I might approach it. // If all of your section views behave similarly // why not create a single base class bApp.view.Section = Backbone.View.extend({ initialize: function(options) { this.template = options.template; }, render: function() { var html = nunjucks.render(this.template); this.$el.html(html);
return this;
},
close: function() {
this.$el.empty(); this.$el.off();
this.stopListening();

// Any other cleanup can go here...
}
});

bApp.Router = Backbone.Route.extend({
routes: {
// Use route parameters, to simplify routing
'book/:ch/:sec': 'navigateToSection'
},

navigateToSection: function(ch, sec) {
// Delegate view-switching logic to application
bApp.show(ch, sec);
}
});

bApp.router = new bApp.Router();

// Give the bApp controller
// power over switching views
bApp.show = function(ch, sec) {
var templatePath = './client/views/client-templates/ch' + ch + '_sec' + sec + '.html';

if (this.currentView) {
// Clean up your old view
this.currentView.close();
}

this.currentView = new bApp.view.Section({
el: 'div#chapter' + ch,
template: templatePath
});

this.currentView.render()

// Update the route
// This may seem redundant, but this will allow
// you to call bApp.show() directly, and keep your
// route up to date.
bApp.route.navigate('book/' + ch + '/' + sec);
}


Here's a good article by Derick Bailey about common pitfalls with Backbone routers.

Edit: Clarify App/Router Separation

Think of the router just as one of many ways to change the state of your application. Just like you could click a "first page" button to go to the first page, you could enter in /page/first in your browser to go to the first page. The only difference is whether your application state is bound to a button, or to a route.

This is the reason to keep the logic that changes the application state (ie. changing the rendered page) out of your router.

Consider this:

bApp.view.Section = Backbone.View.extend({
events: {
'change input.ch]': this.handlePageChange_
'change input.sec]': this.handlePageChange_
}
//...
handlePageChange_ = function() {
var ch = this.$('input.ch').val(); var sec = this.$('input.sec').val();

// Let your application handle state change
bApp.show(ch, sec)
}
});


In this example, you've bound a input element to your application state, just as you bound a route to your application state. Either way, the state-changing logic belongs in your application controller.

• edan, thanks a lot. This is really helpful. Being new to backbone there are a few things I need to figure out, but overall I understand what you are doing. The only part that is not clear is what you mean when you say that I will be able to call bApp.show() directly and about route being up to date. Is the explicit call to route.navigate an alternative to Backbone.history.start()? – Curious2learn Oct 29 '13 at 1:58
• This is pretty much what I was going to say but I usually override remove rather than adding a close. @Curious2learn You'll still need the Backbone.history.start() call to crank up the routing machinery. – mu is too short Oct 29 '13 at 5:44