English |  Español |  Français |  Italiano |  Português |  Русский |  Shqip

Developing a Backbone.js Edge

Chapter 8: Routing

While Models and Collections exist to keep track of the state of your data, you can think of URLs as keeping track of the state of navigation.  In traditional web development, you would create separate HTML files, where they might be called index.html, about.html, books.html, etc.  Navigating between pages caused the URL to change because it was actually loading a separate HTML file, and thus the URL would explicitly tell you where to access that resource.  This concept is part of Representational State Transfer, or REST.  HTML dynamically generated by a server can work in the same way: mysite.com/users/123 refers to a specific user page.  In short: if you send a URL to a friend, you want them to basically see what you are seeing.

In a single-page application, you are creating and removing views programatically, so you need to explicitly tell the browser when the navigational state has changed, and you need to be able to take a particular state representation (a URL) and render the correct thing on the screen.  This is where the Backbone.Router comes in.

Backbone.Router

In Backbone, we use the Router class to manage (and sometimes trigger) these changes in navigational state.  First, create a subclass:

// router.js
(function () {
'use strict';
var app = window.app;
var Backbone = window.Backbone;

app.Router = Backbone.Router.extend({
});
})();

// index.html
<!-- Add after "Router goes here." -->

<!-- Note that Routers will typically be included
after Models/Collections/Views. -->
<script src='router.js'></script>

We put router.js in the top-level the directory, because in Hubbub (and for most single-page apps), you'll only have a single Router defined.  Likewise, it will only be instantiated once, from your application entry-point.  Add this next:

// index.js
init: function () {
  // ...
  app.router = new app.Router();
  Backbone.History.start();
}

Backbone.History is a singleton that manages setting/handling of URL changes, which it then delegates to the Router when there's a match.  The routes are defined as a map in a Backbone.Router subclass, where particular "routing strings" (relative URLs) are connected to particular "actions" (methods).  In our case, we have two navigational states: viewing the lists of Issues (which we will refer to as the "manage" action), and viewing a particular Issue.  Set these up in our Router:

// router.js
app.Router = Backbone.Router.extend({
routes: {
    // the base URL - "managing" the lists of issues
    '': 'manage',
// an alias for the root path
'issues': 'manage',
    // viewing a particular issue
    'issues/:id': 'issue'   },

  manage: function () {
console.log('homepage');
},

  issue: function (issueId) {
    // the :id matched from our route string will be passed in
// as a parameter
console.log('issue ' + issueId);
  }
});

In your browser, open the Console and refresh the page - you should see "homepage" printed out.  Add "#issues/6" to the end of the URL (so it will now be ".../hubbub/index.html#issues/6") and press Enter. You should now see "issue 6" in the Console.  The Router worked!

By default, Backbone uses hash-based navigation, though you can configure it to use pushState for newer browsers if you prefer... see the Backbone.history.start() documentation for more info.

Reflecting State

Now that we have changes to the URL triggerering route actions, we need to update our view(s) to reflect the new state.  Add the following:

// router.js
app.Router = Backbone.Router.extend({
  // ...

  initialize: function (options) {
    // pass in the main view
    this.mainView = options.mainView;
  },

  manage: function () {
    this.mainView.closeModals();
  },
  issue: function (issueId) {
    this.mainView.showIssueModal(issueId);
  }
);

Give it a try!  Refresh the page and click on a particular Issue – you should now not only see the modal appear, but the URL will change to reflect it.  Huzzah!  We now have URL changes triggering View changes via the Router.

Notice that we have a very small amount of logic in our Router.  We recommend having the actions call only a single method, because then it's easy to trigger that change of state from either the Router or elsewhere in the app.  Another advantage of having Backbone update URLs is that your browser will keep track of history. Click the Back button to return to the "manage" view.

Conclusion

Now you see the power of Backbone.router, and the important role it plays tracking the state of navigation with your URLs in Backbone. In the next and final chapter of this book, we will cover topics such as modules, build tools, and how to prepare for production.

There has been error in communication with Booktype server. Not sure right now where is the problem.

You should refresh this page.