I am a software engineer at Adobe working on the Launch product, primarily focusing on the Launch runtime library and extension development ecosystem.

Feb 29, 2012

JavaScript Architecture: Backbone.js Routers

Updated Aug 11, 2012 to reflect current library versions.

In JavaScript Architecture: Backbone.js Views we discussed how to build dynamic apps that change views on the fly using JavaScript. Because view-switching is done without reloading the page or transferring control to a separate page, these are called single-page applications. Single-page applications pose a few issues we need to address:

When users hit their browser’s back button, they will be taken away from the app completely rather than back to a previous view within the app itself.

Users are only able to link to or bookmark the app itself — not a specific view within the app.

Deep views within the app may not be crawlable by search engines.

We want a great experience for our users. Successful apps behave as users would logically expect and users should feel like they can easily navigate back to where they were previously.

Like the topics we’ve addressed before, these issues aren’t specific to Backbone. It’s an issue that naturally arises in any single-page app. Fortunately, Backbone does a great job at addressing it and has a simple API.

Examples

This concept is most easily taught by example so let’s assume we’re building an app that sells shirts. We’ve built it out so we show a grid of shirt images and when the user clicks on a shirt, a panel slides out that covers half of the window and contains more details regarding that specific shirt. Without some extra work, if users are on this extra details view and click the back button in the browser, they will be taken out of the app completely rather than just hiding the extra details view. This is because, technically, the details view is not a new html page — it’s just another “view” within the same html page.

The browser has no concept of views that are changing within your app and therefore cannot automatically register history steps in this regard. As we discussed in JavaScript Architecture: Backbone.js Views, the definition and granularity of a view can become very blurry. For example, if we pop up a settings panel after a user clicks on a gear icon, does the settings panel merit a history step in the browser so that if the user clicks the back button the panel closes? As much as we would like browsers to figure this out automatically, it’s really a user experience judgement call on our part and we must tell the browser when to register a new history step. This gives us a lot of power.

You can see we have three links each linking to a shirt with a different id. Notice that the pound sign at the beginning of each link url (e.g., #shirt/id/5) is important as it tells the browser we’re neither moving to a new html page nor refreshing the current page. It’s formally known as a fragment identifier and has been a web standard for quite a while.

In our example, We’ve also set up a backbone router with “routes” which are essentially url patterns that are meaningful to our app. In this case, we have set up a single route, shirt/id/:id, with a handler of showShirt(). The :id portion is called a parameter part. If a user navigates to the url <current_page>/#shirt/id/123123, the fragment would qualify as a match and the id (123123) would be passed as a parameter to the showShirt() function. In our example, if the user clicks the first link we set up, the url will be changed to <current_page>/#shirt/id/5 and Backbone will subsequently call showShirt(5).

To have a little fun, let’s go nuts for donuts and get crazy up in here:

This time we set up our route with three parameter parts. Because there are three parameter parts, three parameters will be passed into showProduct(). Easy enough.

Deep-linking

Now that we have our routes set up, we can deep-link to a specific view within our app. For example, a user might click on the size 12 shoes link which changes the url in their browser to http://code.aaronhardy.com/backbone-router-multi-param/#shirt/id/5. The user may want to share that shirt with a friend so they copy the link and send it in an email. When the user clicks on the link, the app loads, and backbone sees that the url already matches a route. showProduct() will then be automatically called which will immediately show the product details panel to the user. Go ahead, click on the link above and see the concept in action.

Showing requested content

In the above examples we’re just alerting information about the parameter parts. In a real app, the user would expect the app to switch to an appropriate view and load appropriate data. How you handle going from the showProduct() function to switching views has been left up to you. However, one option is, rather than calling a function within the router itself, views can watch the router for route events and update themselves accordingly: