Posted on by & filed under javascript, programming.

As a UX developer at Safari Books Online, I need to be familiar with all aspects of our front-end architecture. A big part of that is Backbone.js. My JavaScript experience is jQuery plugins and scripts so I’ve needed to play catchup on this Backbone thing. I learn best by pulling up my sleeves and diving into a project. I decided I’d like to work with some kind of API and ended up choosing the Google Books API.

The end result is backbone_books. You can jump to the demo here. Or check out the code on Github here.

backbonebooks

The Backbone components include;

  1.  A book model for storing book data returned in JSON format from the Google Books api
  2.  A book collection for storing a collection of book models
  3.  A “my library” collection hooked up to the backbone.localstorage adapter for storing a collection of book models in localstorage
  4. A router for mapping URL’s (the category links) to a search view for querying the Google Books api
  5.  4 views for displaying the data; an ‘all books view’, a book view, details view and a search view.

Organization

It’s organized with require.js. Each Backbone component is a self-contained ‘module’ that includes only the other components they require. This way you avoid the traditional tangle of ‘script’ tags that load every js file on every page. The file structure for the project looks like this:

index.html
css
img
js
app
collections
models
routers
templates
utils
views
_init.js
libs
backbone
backbone.localstorage
require.min
require.text
app.js

There is just one javascript include in index.html:

This lets require load js/app.js as the primary entry point. App.js sets the root directory for my modules and maps third party libraries to ‘namespaced’ module IDs. These get loaded asynchronously in the order of their dependencies.

In my app/ folder I’ve got all the main Backbone components organized nicely into their own folders.

At the bottom of app.js, it loads _init.js inside the app folder.

_init initializes the router and starts the Backbone history for bookmarkable url’s.

The router maps hash urls with query parameters (‘:query’) to functions that start new searches. The “query” parameters can be named anything. If some of the searches look mostly the same it’s because they are. The Google API docs ask for example that you search for authors by prepending “:inauthor” to the search term. Other parameters I need to pass include the maximum books I want to show per query, the search term (obviously), and the index. For example new searches begin at a ‘0’ index – if I want to search again for more books and I’m showing ten books, the index should start at ’11’. This is tracked in the search view.

Inside the router.

The index page kicks it off by mapping the page with no hash urls to a new search. The search view initializes some topics and listens to form submissions on the search form. This kicks off a search on its value and does an ajax query on the Google Books API.

The ajax response data gets added into a new model then into a collection.

Once I have a book collection with a bunch of models, I need to display them. So I instantiate the “AllBooksView” and pass it the collection.

Inside the AllBooksView,

I pass each book model to its own view for rendering (BookView). This is because I want to bind it to a click event that passes it’s specific model data to the details view. This way the details view can grab its volume ID from the model which I need to query the Google Books API for its full detail data.

The book view (which created the book thumbnails) listens to clicks and passes its model to a details view.

Inside the BookView,

Views can be assigned an ‘el’ which attaches the view to that html in the DOM (similar to document.getElementById(‘#search_form’)). Otherwise, views can be assigned a ‘tagName’ – in which case it creates the HTML and prepends itself to it. In the BookView’s case, it’s created inside an “li” html list. If you look in the AllBooksView, the tagName is “ul”, so new books are appended to the “ul” created by the AllBooksViews.

BookView instantiates the DetailView on the #book-details html element already in the dom and calls it’s render method.

The details view checks if the model is in localstorage by looping through the localstorage keys and looking for its volume id (namespaced to ‘myBooks’).

localstorage
Inside the DetailsView,

If the book exists in local storage, fetch it from there and display it — otherwise do an API query.

bookdetail

Once the book details are displayed, you can either remove it from localstorage or save it to localstorage.

It’s not the prettiest code. I’ve intentionally included only snippets and linked to the full modules so as to illustrate the overall process rather then get you bogged down in the details. Some of that extra code is silly show/hide animations. Other stuff is for defining JSON objects to prevent errors or silly things like checking the window.location.hash for a router URL. Debugging I check console.log() a lot and these things arise out of that.

Backbone is great fun. I got a lot of good information from Addy Osmani’s book. If you’re new to Backbone and want to learn it I recommend reading up on it and then start something from scratch, check the console.log(), check the backbone docs, underscore.js has some really useful methods (and is required by backbone) — go slow and things should start to make sense.

Tags: APIs, Backbone, google books, Javascript,

Comments are closed.