Last night, I was working on a little demonstration project, teaching myself the intricacies of Backbone/CouchDB interaction. I wrote my first CouchDB views and figured out what they’re for, which is a massive step toward world domination and all that. I was working on the retrieval layer, and thinking about the create/update features, when I said to myself, “Hey, Self, can you use that sexy new RDFa stuff to handle the create/update feature?”

I’ve been thinking about this because I have a different project that’s very RDFa-heavy, and the details of implementation have been challenging. But no, I had to come to a different conclusion:

RDFa and Backbone.js are incompatible.

RDF is a framework for self-describing data, such that the data carries along with itself tokenized indicators for how it is to be organized, presented, and edited. The idea is simple: the browser requests a page, the server sends it all: the HTML represents the structured data, the CSS the visual depiction, and the javascript detects the RDFA (the ‘A’ stands for ‘attributes’, HTML attributes) and decorates these islands of content with controls to make the content editable and saveable back to the server.  The data itself is kept in a representative semantic form of (X)HTML trees.

Backbone, in contrast, is a software framework in which the data is represented in a tree-like structure in Javascript itself, in the models and collections. The data does not come down with the page, but is requested after the fact by Backbone.  The HTML is rendered as a side-effect (my Haskell classes are rubbing off on me): it is a representation of the data-state on the client. Backbone works hard under the covers to keep the client and server state in sync, but in all cases the HTML is just a representation of the data; it is not the data itself.  The data itself is kept in Javascript trees.

These are two completely different approaches to managing data on the client. Both are valid. And both have their uses, but for the moment they don’t mesh well.

“If you have an idea and publish it on the internet, it counts as the ghost of done.”

So here’s my ghost:

We need a Backbone-like for RDFa. RDFa cannot easily be coerced into working with Backbone, but let’s face it: there are only a few things we work with on the internet: text and inputs, and lists and trees of text and inputs.

It ought to be possible to formalize a backbone-like collection of Views, Schemas and Rules, such that the transition from presentation to revision is seamless and perhaps not even distinct, and synchronization for a range of RDFa-enabled articles, forms, fieldsets, and the like is automagical. In other works, make Models and Views “know” about their data because it’s already present in the HTML, and not the other way around.  It’s the transition from page-format to server-side format that’s the pain point.

Hmm. This needs more thinking.

I got a call last night from a recruiter who was looking for “A front end guy: Javascript, HTML, CSS.  But the real kicker is they need someone with experience in Backbone.  Do you know anyone with that?”

I said, “Sure. Me.” I told him about the tutorials I had written for Backbone, the current use of Backbone at ${DAY_JOB}, the stuff I had contributed to Backbone-relational, and for a while we talked about the difference between Backbone and Knockout on the one hand, and then those versus do-everything frameworks like Cappucino and Sproutcore.

But here’s what gets me wondering: is Backbone.js, a fairly small and smart library, by itself difficult enough to warrant its own recruitment category? Any competent Javascript programmer who’s had any experience writing enterprise-level code, working with any of the above frameworks (along with JavascriptMVC and Batman and… did Amber ever make it into the wild?  I can’t follow everything, you know!), should be able to get up-to-speed on Backbone within a week or so of using it.  Just write a tutorial, like I did, then re-write to figure out how silly your original try was.

Is “We need Backbone experience” even a reasonable request at this point?

It’s no secret that I’m a Backbone.js fan, along with everything else. One of my other preferences is for Backbone-Relational, a nifty hack on top of Backbone that allows you to create, manage, and maintain simple relationships between objects. If you have a CouchDB or other JSON-oriented database, and you want to manipulate a record with Backbone on the client and then write it back, Backbone-Relational will let you define how child records get encoded into JSON, and when you write the parent object the backbone toJSON() call will automatically encapsulate your record in a DB-ready format. Very good.

I’m also fond of namespaces, and have started using the easy modules hack to manage namespaces with Coffeescript. It’s just a layer of abstraction on top of simply allocating yourself a namespace hanging off the root (in the client case, the traditional DOM window object). But it’s a nice, clear, self-documenting abstraction.

I discovered, however, that Backbone-Relational doesn’t like this idea. Since it’s an ORM, it needs to keep references to the models used for relationships, and it stores these as strings. The introspection layer is fairly dumb and not sure where to stop, so it assumes all of the names hang off the root namespace.

Bad idea. The following will not work:

@module "MyModule", ->
    class @AClass
        relations: [
            type: Backbone.HasMany
            key: 'children'
            relatedModel: @TheOtherClass
            includeInJSON: false
            reverseRelation:
                type: Backbone.HasOne
                key: 'parent'
        ]

However, because Backbone-Relational is working off string namespaces, specifying the relationships a  string works wonders:

@module "MyModule", ->
    MyModule = @
    class MyModule.AClass
        relations: [
            type: Backbone.HasMany
            key: 'children'
            relatedModel: "MyModule.TheOtherClass"
            includeInJSON: false
            reverseRelation:
                type: Backbone.HasOne
                key: 'parent'
        ]

In some respects, this is fairly ugly. You’re losing some of the shorthand advantages of using the Module hack by repetitiously (if you’re using the namespace in more than one file) re-assigning the module’s this object to a local name.  In other respects, it’s self-documenting, allows Backbone-Relational to work well, and is well-behaved.

Introduction To Version 2.0, Modern Edition.

When I wrote the original Backbone Store, I didn’t have a clear understanding of how Backbone worked.  I wrote it to teach myself Backbone, after all.  Since then, I’ve come to understand that there is some Javascript about which I didn’t have the world’s clearest understanding.  This version of the store clears up those misconceptions and illustrates a clear separation between the data layer and the presentation layer.  Because of this clarity, it looks much less like the original JSON store tutorial for Sammy.js.

This particular document shows my current work kit.  I have provided a version of The Backbone Store 2.0 for main-line coders who still use Javascript, HTML and CSS with Backbone, but after doing so I decided to re-write all of my code to show how I really work, with Coffeescript, HAML, Stylus, and Backbone.  This is the result.

CoffeeScript is a lovely little languange that compiles into Javascript. It provides a class-based architecture (that is compatible with Backbone), has an elegant structure for defining functions and methods, and strips out as much extraneous punctuation as possible. Some people find the whitespace-as-semantics a’la Python offputting, but most disciplined developers already indent appropriately and find using Coffeescript a breeze.

HAML is a languange that compiles into HTML. Like CoffeeScript, it uses whitespace for semantics: indentation levels correspond to HTML containerizations. It allows you to use rich scripting while preventing heirarchy misplacement mistakes. Its shorthand also makes writing HTML much faster.

Stylus is languange that compiles into CSS. Like CoffeeScript and HAML, it uses whitespace for semantics.  (If you’re detecting a theme here, you should know by now that I’m a Python partisan.)  It also provides mixins and functions that allow you to define visual styles such as borders and gradients, and mix them into specific selectors in the CSS rather than having to write them into the HTML.

Backbone.js is a popular Model-View-Controller (MVC) library that provides a framework for creating data-rich, single-page web applications. It provides (1) a two-layer scheme for separating data from presentation, (2) a means of automatically synchronizing data with a server in a RESTful manner, and (3) a mechanism for making some views bookmarkable and navigable.

There are a number of other good tutorials for Backbone (See: Meta Cloud, &Yet’s Tutorial, Backbone Mobile (which is written in Coffee), and Backbone and Django. However, a couple of months ago I was attempting to learn Sammy.js, a library very similar to Backbone, and they had a nifty tutorial called The JsonStore.

In the spirit of The JSON Store, I present The Backbone Store, Modern Edition.

Literate Program

A note: this article was written with the Literate Programming toolkit Noweb. Where you see something that looks like <<this>>, it’s a placeholder for code described elsewhere in the document. Placeholders with an equal sign at the end of them indicate the place where that code is defined. The link (U->) indicates that the code you’re seeing is used later in the document, and (<-U) indicates it was used earlier but is being defined here.

Revision

This is version 2.0 of The Backbone Store. It includes changes to the store based upon a better understanding of what Backbone.js can do. This version uses jQuery 1.6.2 and Backbone 0.5.2.

The Store

To demonstrate the basics of Backbone, I’m going to create a simple one-page application, a store for record albums, with two unique views: a list of all products and a product detail view. I will also put a shopping cart widget on the page that shows the user how many products he or she has dropped into the cart. I’ll use jQuery’s fadeIn() and fadeOut() features to transition between the catalog and the product detail pages.

Models, Collections, and Controllers

Backbone’s data layer provides two classes, Model and Collection. To use the Model, you inherit from it, modify the subclasss as needed, and then create new objects from the subclass by constructing the model with a JSON object. You modify the object by calling get() or set() on named attributes, rather than on the Model object directly; this allows Model to notify other interested objects that the object has been changed. And Model comes with fetch() and save() methods that will automatically pull or push a JSON representatino of the model to a server, if the Model has url as one of its attributes.

Collections are just that: lists of objects of a specific model. You extend the Collection class in a child class, and as you do you inform the Collection of what Model it represents, what URL you use to push/pull the full list of objects, and on what field the list should be sorted by default. If you attempt to add a raw JSON object to a collection, it constructs a corresponding Model object out of the JSON and manipulates that.

I will be getting the data from a simplified JSON file that comes in the download; it contains six record albums that the store sells. (Unlike the JSON store, these albums do not exist; the covers were generated during a round of The Album Cover Game, a meme one popular with graphic designers.)

For our purposes, then, we have a Product and a ProductCollection. A popular convention in Backbone is to use concrete names for models, and NameCollection for the collection.

Models are duck-typed by default; they do not care what you put into them. So all I need to say is that a Product is-a Model. The Collection is straightforward as well; I tell it what model it represents, override the initialize() method (which is empty in the Backbone default) to inform this Collection that it has a url, and create the comparator function for default sorting.

Note that Coffeescript uses ‘@’ to represent this, and always returns the last lvalue generated by every function and method. So the last line of initialize below compiles to return this.

<product models>= (U->)
class Product extends Backbone.Model

class ProductCollection extends Backbone.Collection
    model: Product

    initialize: (models, options) ->
        @url = options.url
        @

    comparator: (item) ->
        item.get('title')

For the shopping cart, our cart will hold Items, and the cart itself will be an ItemCollection. Shoppings carts are a little odd; the convention is that an Item is not a single instance of a product, but a reference to the products and a quantity.

One thing we will be doing is changing the quantity, so I have provided a convenience function for the Item that allows you to do that. Now, no client classes such as Views need to know how the quantity is updated.

Also, it would be nice to know the total price of the Item.

<cart models>= (U->) [D->]
class Item extends Backbone.Model
    update: (amount) ->
        if amount == @get('quantity')
            return
        @set {quantity: amount}, {silent: true}
        @collection.trigger('change', this)

    price: () ->
        @get('product').get('price') * @get('quantity')

The ItemCollection is a little trickier. It is entirely client-side; it has no synchronization with the backend at all. But it does have a model.

The ItemCollection must be able to find an Item in the cart to update when a view needs it. If the Item is not in the Collection, it must create one. The method getOrCreateItemForProduct does this. It uses the detect() method, a method Collection inherits from Backbone’s one dependency, Underscore.js; detect() returns the first Item in the ItemCollection for which the function returns true. Also, when I have to create a new Item, I want to add it to the collection, and I pass the parameter silent, which prevents the Collection from notifying event subscribers that the collection has changed. Since this is an Item with zero objects in it, this is not a change to what the collection represents, and I don’t want Views to react without having to.

Finally, I add two methods that return the total count of objects in the collection (not Items, but actual Products), and the total cost of those items in the cart. The Underscore method reduce() does this by taking a function for adding progressive items, and a starting value.

<cart models>+= (U->) [<-D]
class ItemCollection extends Backbone.Collection
    model: Item

    getOrCreateItemForProduct: (product) ->
        pid = product.get('id')
        i = this.detect (obj) -> (obj.get('product').get('id') == pid)
        if (i)
            return i
        i = new Item
            product: product
            quantity: 0
        @add i, {silent: true}
        i

    getTotalCount: () ->
        addup = (memo, obj) -> obj.get('quantity') + memo
        @reduce addup, 0

    getTotalCost: () ->
        addup = (memo, obj) ->obj.price() + memo
        @reduce(addup, 0);

Views

Backbone Views are simple policy objects. They have a root DOM element, the contents of which they manipulate and to which they listen for events, and a model or collection they represent within that element. Views are not rigid; it’s just Javascript and the DOM, and you can hook external events as needed.

More importantly, a View is sensitive to events within its model or collection, and can respond to changes automatically. This way, if you have a rich data ecosystem, when changes to one data item results in a cascade of changes throughout your datasets, the views will receive “change” events and can update themselves accordingly.

I will show how this works with the shopping cart widget.

To achieve the fadeIn/fadeOut animations and enforce consistency, I’m going to do some basic object-oriented programming. I’m going to create a base class that contains knowledge about the main area into which all views are rendered, and that manages these transitions.

With this technique, you can do lots of navigation-related tricks: you can highlight where the user is in breadcrumb-style navigation; you can change the class and highlight an entry on a nav bar; you can add and remove tabs from the top of the viewport as needed.

<base view>= (U->) [D->]
class _BaseView extends Backbone.View
    parent: $('#main')
    className: 'viewport'

The above says that I am creating a class called BaseView and defining two fields. The first, ‘parent’, will be used by all child views to identify into which DOM object the View root element will be rendered. The second defines a common class we will use for the purpose of identifying these views to jQuery. Backbone automatically creates a new DIV object with the class ‘viewport’ when a view constructor is called. It will be our job to attach that DIV to the DOM. In the HTML, you will see the DIV\#main object where most of the work will be rendered.

<base view>+= (U->) [<-D->]
    initialize: () ->
        @el = $(@el)
        @el.hide()
        @parent.append(@el)
        @


The method above ensures that the element is rendered, but not visible, and contained within the DIV\#main. Note also that the element is not a sacrosanct object; the Backbone.View is more a collection of standards than a mechanism of enforcement, and so defining it from a raw DOM object to a jQuery object will not break anything.

Next, we will define the hide and show functions.

Note that in coffeescript, the => operator completely replaces the _.bind() function provided by underscore.

<base view>+= (U->) [<-D]
    hide: () ->
        if not @el.is(':visible')
            return null
        promise = $.Deferred (dfd) => @el.fadeOut('fast', dfd.resolve)
        promise.promise()

    show: () ->
        if @el.is(':visible')
            return

        promise = $.Deferred (dfd) => @el.fadeIn('fast', dfd.resolve)
        promise.promise()

Deferred is a new feature of jQuery. It is a different mechanism for invoking callbacks by attaching attributes and behavior to the callback function. By using this, we can say thing like When everything is hidden (when every deferred returned from hide has been resolved), then show the appropriate viewport.” Deferreds are incredibly powerful, and this is a small taste of what can be done with them.

Before we move on, let’s take a look at the HAML we’re going to use for our one-page application. The code below compiles beautifully into the same HTML seen in the original Backbone Store.

<index.haml>=
!!! 5
%html{:xmlns => "http://www.w3.org/1999/xhtml"}
  %head
    %title The Backbone Store
    %link{:charset => "utf-8", :href => "jsonstore.css", :rel => "stylesheet", :type => "text/css"}/
    <product list template>
    <product detail template>
    <cart template>
    </head>
  %body
    #container
      #header
        %h1
          The Backbone Store
        .cart-info
      #main
    %script{:src => "jquery-1.6.2.min.js", :type => "text/javascript"}
    %script{:src => "underscore.js", :type => "text/javascript"}
    %script{:src => "backbone.js", :type => "text/javascript"}
    %script{:src => "store.js", :type => "text/javascript"}

It’s not much to look at, but already you can see where that DIV\#main goes, as well as where we are putting our templates. The DIV\#main will host a number of viewports, only one of which will be visible at any given time.

Our first view is going to be the product list view, named, well, guess. Or just look down a few lines.

This gives us a chance to discuss one of the big confusions new Backbone users frequently have: What is render() for?. Render is not there to show or hide the view. Render() is there to change the view when the underlying data changes. It renders the data into a view. In our functionality, we use the parent class’s show() and hide() methods to actually show the view.

That call to \_super\_ is a Backbone idiom for calling a method on the parent object. It is, as far as anyone knows, the only way to invoke a superclass method if it has been redefined in a subclass. It is rather ugly, but useful.

<product list view>= (U->)
class ProductListView extends _BaseView
    id: 'productlistview'
    template: $("#store_index_template").html()

    initialize: (options) ->
        @constructor.__super__.initialize.apply @, [options]
        @collection.bind 'reset', _.bind(@render, @)

    render: () ->
        @el.html(_.template(@template, {'products': @collection.toJSON()}))
        @


That _.template() method is provided by undescore.js, and is a full-featured, javascript-based templating method. It’s not the fastest or the most feature-complete, but it is more than adequate for our purposes and it means we don’t have to import another library. It vaguely resembles ERB from Rails, so if you are familiar with that, you should understand this fairly easily.

And here is the HAML:

<product list template>= (<-U)
%script#store_index_template(type="text/x-underscore-tmplate")
  %h1 Product Catalog
  %ul
    <% for(i=0,l=products.length;i<l;++i) { p = products[i]; %>
    %li.item
      .item-image
        %a{:href => "#item/<%= p.id %>"}
          %img{:src => "<%= p.image %>", :alt => "<%= p.title %>"}/
      .item-artist <%= p.artist %>
      .item-title <%= p.title %>
      .item-price $<%= p.price %>
    <% } %>


One of the most complicated objects in our ecosystem is the product view. It actually does something! The prefix ought to be familiar, but note that we are again using \#main as our target; we will be showing and hiding the various DIV objects in \#main again and again.

The only trickiness here is twofold: the (rather hideous) means by which one calls the method of a parnt class from a child class via Backbone’s class heirarchy (this is most definitely not Javascript standard), and keeping track of the itemcollection object, so we can add and change items as needed.

<product detail view>= (U->) [D->]
class ProductView extends _BaseView
    id: 'productitemview'
    template: $("#store_item_template").html()
    initialize: (options) ->
            @constructor.__super__.initialize.apply @, [options]
            @itemcollection = options.itemcollection
            @item = @itemcollection.getOrCreateItemForProduct @model
            @

There are certain events in which we’re interested: keypresses and clicks on the update button and the quantity form. (Okay, “UQ” isn’t the best for “update quantity”. I admit that.) Note the peculiar syntax of “EVENT SELECTOR”: “methodByName” for each event.

Backbone tells us that the only events it can track by itself are those that jQuery’s “delegate” understands. As of 1.5, that seems to be just about all of them.

<product detail view>+= (U->) [<-D->]
    events:
        "keypress .uqf" : "updateOnEnter"
        "click .uq"     : "update"

And now we will deal with the update. This code ought to be fairly readable: the only specialness is that it’s receiving an event, and we’re “silencing” the call to cart.add(), which means that the cart collection will not publish any events. There are only events when the item has more than zero, and that gets called on cart_item.update().

In the original tutorial, this code had a lot of responsibility for managing the shopping cart, looking into it and seeing if it had an item for this product, and there was lots of accessing the model to get its id and so forth. All of that has been put into the shopping cart model, which is where it belongs: knowledge about items and each item’s relationship to its collection belongs in the collection.

Look closely at the update() method. The reference @\$ is a special Backbone object that limits selectors to objects inside the element of the view. Without it, jQuery would have found the first input field of class ‘uqf’ in the DOM, not the one for this specific view. @\$('.uqf') is shorthand for $('uqf', @el), and helps clarify what it is you’re looking for.

<product detail view>+= (U->) [<-D->]
    update: (e) ->
        e.preventDefault()
        @item.update parseInt(@$('.uqf').val())

    updateOnEnter: (e) ->
        if (e.keyCode == 13)
            @update e


The render is straightforward:

<product detail view>+= (U->) [<-D]
    render: () ->
        @el.html(_.template(@template, @model.toJSON()))
        @

The product detail template is fairly straightforward. There is no underscore magic because there are no loops.

<product detail template>= (<-U)
%script#store_item_template(type= "text/x-underscore-template")
  .item-detail
    .item-image
      %img(src="<%= large_image %>" alt="<%= title %>")/
    .item-info
      .item-artist <%= artist %>
      .item-title <%= title %>
      .item-price $<%= price %>
      .item-form
      %form(action="#/cart" method="post")
        %p
          %label Quantity:
          %input(type="text" size="2" name="quantity" value="1" class="uqf")/
        %p
          %input(type="submit" value="Add to Cart" class="uq")/

      .item-link
        %a(href="<%= url %>") Buy this item on Amazon
      .back-link
        %a(href="#") &laquo; Back to Items

So, let’s talk about that shopping cart thing. We’ve been making the point that when it changes, when you call item.update within the product detail view, any corresponding subscribing views sholud automatically update.

<cart widget>= (U->) [D->]
class CartWidget extends Backbone.View
    el: $('.cart-info')
    template: $('#store_cart_template').html()

    initialize: () ->
        @collection.bind('change', _.bind(@render, @));


And there is the major magic. CartWidget will be initialized with the ItemCollection; when there is any change in the collection, the widget will receive the ‘change’ event, which will automatically trigger the call to the widget’s render() method.

The render method will refill that widget’s HTML with a re-rendered template with the new count and cost, and then wiggle it a little to show that it did changed:

<cart widget>+= (U->) [<-D]
    render: () ->
        tel = @el.html _.template @template,
            'count': @collection.getTotalCount()
            'cost': @collection.getTotalCost()
        tel.animate({paddingTop: '30px'}).animate({paddingTop: '10px'})
        @

And the HTML for the template is dead simple:

<cart template>= (<-U)
%script#store_cart_template(type="text/x-underscore-template")
  %p Items: <%= count %> ($<%= cost %>)


Lastly, there is the Router. In Backbone, the Router is a specialized View for invoking other views. It listens for one specific event: when the window.location.hash object, the part of the URL after the hash symbol, changes. When the hash changes, the Router invokes an event handler. The Router, since its purpose is to control the major components of the one-page display, is also a good place to keep all the major components of the sytem. We’ll keep track of the Views, the ProductCollection, and the ItemCollection.

<router>= (U->) [D->]
class BackboneStore extends Backbone.Router
    views: {}
    products: null
    cart: null

There are two events we care about: view the list, and view a detail. They are routed like this:

<router>+= (U->) [<-D->]
    routes:
        "": "index"
        "item/:id": "product"

Like most Backbone objects, the Router has an initialization feature. I create a new, empty shopping cart and corresponding cart widget, which doesn’t render because it’s empty. I then create a new ProductCollection and and corresponding ProductListView. These are all processes that happen immediately.

What does not happen immediately is the fetch() of data from the back-end server. For that, I use the jQuery deferred again, because fetch() ultimately returns the results of sync(), which returns the result of an ajax() call, which is a deferred.

<router>+= (U->) [<-D->]
    initialize: (data) ->
        @cart = new ItemCollection()
        new CartWidget
            collection: @cart

        @products = new ProductCollection [],
            url: 'data/items.json'
        @views =
            '_index': new ProductListView
                collection: @products
        $.when(@products.fetch({reset: true}))
            .then(() -> window.location.hash = '')
        @


There are two things to route to, but we must also route from. Remember that our two major views, the product list and the product detail, inherited from \_BaseView, which has the hide() and show() methods. We want to hide all the views, then show the one invoked. First, let’s hide every view we know about. hide() returns either a deferred (if the object is being hidden) or null. The _.select() call at the end means that this method returns only an array of deferreds.

<router>+= (U->) [<-D->]
    hideAllViews: () ->
        _.select(_.map(@views, (v) -> return v.hide()),
            (t) -> t != null)

Showing the product list view is basically hiding everything, then showing the index:

<router>+= (U->) [<-D->]
    index: () ->
        view = @views['_index']
        $.when(@hideAllViews()).then(() -> view.show())


On the other hand, showing the product detail page is a bit trickier. In order to avoid re-rendering all the time, I am going to create a view for every product in which the user shows interest, and keep it around, showing it a second time if the user wants to see it a second time.

Not that we pass it the ItemCollection instance. It uses this to create a new item, which (if you recall from our discussion of getOrCreateItemForProduct()) is automagically put into the collection as needed. Which means all we need to do is update this item and the item collection changes, which in turn causes the CartWidget to update automagically as well.

<router>+= (U->) [<-D]
    product: (id) ->
        product = @products.detect (p) -> p.get('id') == (id)
        view = (@views['item.' + id] ||= new ProductView(
            model: product,
            itemcollection: @cart
        ).render())
        $.when(@hideAllViews()).then(
            () -> view.show())


Finally, we need to start the program

<initialization>= (U->)
$ ->
    new BackboneStore()
    Backbone.history.start()

 

The Program

Here’s the entirety of the program. Coffeescript provides its own namespace wrapper:

<store.coffee>=
<product models>

<cart models>

<base view>

<product list view>

<product detail view>

<cart widget>

<router>

<initialization>

Compiling

None of these formats are native to the browser, so getting them into the browser requires a compilation step. Here are the basic compilation steps for these three pieces of code:

haml --unix-newlines --no-escape-attrs --double-quote-attribute index.haml > index.html
coffee -compile store.coffee
stylus jsonstore.styl

I hope in the future to provide a Cakefile (a makefile alternative written in Cake) for your amusement.

A Little Stylus

Stylus is a beautiful little language that compiles down to CSS. The original version of The Backbone Store used the same CSS provided from the original Sammy tutorial, but I wanted to show you this one extra tool because it’s an essential part of my kit.

If you want rounded borders, you know that writing all that code, for older browsers as well as modern ones, and providing it to all the different objects you want styled that way, can be time consuming. Stylus allows you to define a function that can be called from within any style, thus allowing you to define the style here, and attach a set style to a semantic value in your HTML:

<jsonstore.styl>= [D->]
rounded(radius)
  -moz-border-radius-topleft: radius
  -moz-border-radius-topright: radius
  -moz-border-radius-bottomleft: radius
  -moz-border-radius-bottomright: radius
  -webkit-border-bottom-right-radius: radius
  -webkit-border-top-left-radius: radius
  -webkit-border-top-right-radius: radius
  -webkit-border-bottom-left-radius: radius
  border-bottom-right-radius: radius
  border-top-left-radius: radius
  border-top-right-radius: radius
  border-bottom-left-radius: radius

background_gradient(base)
  background: base
  background: -webkit-gradient(linear, left top, left bottom, from(lighten(base, 20%)), to(darken(base, 20%)))
  background: -moz-linear-gradient(top,  lighten(base, 20%), darken(base, 20%))

And if you look down below you’ll see the rounded() function called for the list items, which have borders.

One of the real beauties of Stylus is that you can contains some style definitions within others. You can see below that the header contains an H1, and the H1 definitions will be compiled to only apply within the context of the header. Stylus allows you to write CSS the way you write HTML!

<jsonstore.styl>+= [<-D]
body
  font-family: "Lucida Grande", Lucida, Helvetica, Arial, sans-serif
  background: #FFF
  color: #333
  margin: 0px
  padding: 0px

#header
  background_gradient(#999)
  margin: 0px
  padding: 20px
  border-bottom: 1px solid #ccc

  h1
    font-family: Inconsolata, Monaco, Courier, mono
    color: #FFF
    margin: 0px

  .cart-info
    position: absolute
    top: 0px
    right: 0px
    text-align: right
    padding: 10px
    background_gradient(#555)
    color: #FFF
    font-size: 12px
    font-weight: bold

img
  border: 0

#productlistview
  ul
    list-style: none

.item
  float:left
  width: 250px
  margin-right: 10px
  margin-bottom: 10px
  padding: 5px
  rounded(5px)
  border: 1px solid #ccc
  text-align:center
  font-size: 12px

.item-title
  font-weight: bold

.item-artist
  font-weight: bold
  font-size: 14px

.item-detail
  margin: 10px 0 0 10px

  .item-image
    float:left
    margin-right: 10px

  .item-info
    padding: 100px 10px 0px 10px

And that’s it. Put it all together, and you’ve got yourself a working Backbone Store.

This code is available at my github at The Backbone Store, modern branch.  This branch includes the current Makefile, as well as the native noweb document.

Introduction To Version 2.0

When I wrote the original Backbone Store, I didn’t have a clear understanding of how Backbone worked.  I wrote it to teach myself Backbone, after all.  Since then, I’ve come to understand that there is some Javascript about which I didn’t have the world’s clearest understanding.  This version of the store clears up those misconceptions and illustrates a clear separation between the data layer and the presentation layer.  Because of this clarity, it looks much less like the original JSON store tutorial for Sammy.js.

Backbone.js is a popular Model-View-Controller (MVC) library that provides a framework for creating data-rich, single-page web applications. It provides (1) a two-layer scheme for separating data from presentation, (2) a means of automatically synchronizing data with a server in a RESTful manner, and (3) a mechanism for making some views bookmarkable and navigable.

There are a number of other good tutorials for Backbone (See: Meta Cloud, &Yet’s Tutorial, Backbone Mobile (which is written in Coffee), and Backbone and Django. However, a couple of months ago I was attempting to learn Sammy.js, a library very similar to Backbone, and they had a nifty tutorial called The JsonStore.

In the spirit of The JSON Store, I present The Backbone Store.

Literate Program

A note: this article was written with the Literate Programming toolkit Noweb. Where you see something that looks like <<this>>, it’s a placeholder for code described elsewhere in the document. Placeholders with an equal sign at the end of them indicate the place where that code is defined. The link (U->) indicates that the code you’re seeing is used later in the document, and (<-U) indicates it was used earlier but is being defined here.

Revision

This is version 2.0 of The Backbone Store. It includes changes to the store based upon a better understanding of what Backbone.js can do. This version uses jQuery 1.6.2 and Backbone 0.5.2.

The Store

To demonstrate the basics of Backbone, I’m going to create a simple one-page application, a store for record albums, with two unique views: a list of all products and a product detail view. I will also put a shopping cart widget on the page that shows the user how many products he or she has dropped into the cart. I’ll use jQuery’s fadeIn() and fadeOut() features to transition between the catalog and the product detail pages.

Models, Collections, and Controllers

Backbone’s data layer provides two classes, Model and Collection. To use the Model, you inherit from it, modify the subclasss as needed, and then create new objects from the subclass by constructing the model with a JSON object. You modify the object by calling get() or set() on named attributes, rather than on the Model object directly; this allows Model to notify other interested objects that the object has been changed. And Model comes with fetch() and save() methods that will automatically pull or push a JSON representatino of the model to a server, if the Model has url as one of its attributes.

Collections are just that: lists of objects of a specific model. You extend the Collection class in a child class, and as you do you inform the Collection of what Model it represents, what URL you use to push/pull the full list of objects, and on what field the list should be sorted by default. If you attempt to add a raw JSON object to a collection, it constructs a corresponding Model object out of the JSON and manipulates that.

I will be getting the data from a simplified JSON file that comes in the download; it contains six record albums that the store sells. (Unlike the JSON store, these albums do not exist; the covers were generated during a round of The Album Cover Game, a meme one popular with graphic designers.)

For our purposes, then, we have a Product and a ProductCollection. A popular convention in Backbone is to use concrete names for models, and NameCollection for the collection.

Models are duck-typed by default; they do not care what you put into them. So all I need to say is that a Product is-a Model. The Collection is straightforward as well; I tell it what model it represents, override the initialize() method (which is empty in the Backbone default) to inform this Collection that it has a url, and create the comparator function for default sorting.

<product models>= (U->)
    var Product = Backbone.Model.extend({})

    var ProductCollection = Backbone.Collection.extend({
        model: Product,

        initialize: function(models, options) {
            this.url = options.url;
        },

        comparator: function(item) {
            return item.get('title');
        }
    });

For the shopping cart, our cart will hold Items, and the cart itself will be an ItemCollection. Shoppings carts are a little odd; the convention is that an Item is not a single instance of a product, but a reference to the products and a quantity.

One thing we will be doing is changing the quantity, so I have provided a convenience function for the Item that allows you to do that. Now, no client classes such as Views need to know how the quantity is updated.

Also, it would be nice to know the total price of the Item.

<cart models>= (U->) [D->]
    var Item = Backbone.Model.extend({
        update: function(amount) {
            this.set({'quantity': amount}, {silent: true});
            this.collection.trigger('change', this);
        },
        price: function() {
            console.log(this.get('product').get('title'), this.get('quantity'));
            return this.get('product').get('price') * this.get('quantity');
        }
    });

The ItemCollection is a little trickier. It is entirely client-side; it has no synchronization with the backend at all. But it does have a model.

The ItemCollection must be able to find an Item in the cart to update when a view needs it. If the Item is not in the Collection, it must create one. The method getOrCreateItemForProduct does this. It uses the detect() method, a method Collection inherits from Backbone’s one dependency, Underscore.js; detect() returns the first Item in the ItemCollection for which the function returns true. Also, when I have to create a new Item, I want to add it to the collection, and I pass the parameter silent, which prevents the Collection from notifying event subscribers that the collection has changed. Since this is an Item with zero objects in it, this is not a change to what the collection represents, and I don’t want Views to react without having to.

Finally, I add two methods that return the total count of objects in the collection (not Items, but actual Products), and the total cost of those items in the cart. The Underscore method reduce() does this by taking a function for adding progressive items, and a starting value.

<cart models>+= (U->) [<-D]
    var ItemCollection = Backbone.Collection.extend({
        model: Item,

        getOrCreateItemForProduct: function(product) {
            var i,
            pid = product.get('id'),
            o = this.detect(function(obj) {
                return (obj.get('product').get('id') == pid);
            });
            if (o) {
                return o;
            }
            i = new Item({'product': product, 'quantity': 0})
            this.add(i, {silent: true})
            return i;
        },

        getTotalCount: function() {
            return this.reduce(function(memo, obj) {
                return obj.get('quantity') + memo; }, 0);
        },

        getTotalCost: function() {
            return this.reduce(function(memo, obj) {
                return obj.price() + memo; }, 0);
        }
    });

Views

Backbone Views are simple policy objects. They have a root DOM element, the contents of which they manipulate and to which they listen for events, and a model or collection they represent within that element. Views are not rigid; it’s just Javascript and the DOM, and you can hook external events as needed.

More importantly, a View is sensitive to events within its model or collection, and can respond to changes automatically. This way, if you have a rich data ecosystem, when changes to one data item results in a cascade of changes throughout your datasets, the views will receive “change” events and can update themselves accordingly.

I will show how this works with the shopping cart widget.

To achieve the fadeIn/fadeOut animations and enforce consistency, I’m going to do some basic object-oriented programming. I’m going to create a base class that contains knowledge about the main area into which all views are rendered, and that manages these transitions.

With this technique, you can do lots of navigation-related tricks: you can highlight where the user is in breadcrumb-style navigation; you can change the class and highlight an entry on a nav bar; you can add and remove tabs from the top of the viewport as needed.

<base view>= (U->) [D->]
    var _BaseView = Backbone.View.extend({
        parent: $('#main'),
        className: 'viewport',

The above says that I am creating a class called BaseView and defining two fields. The first, ‘parent’, will be used by all child views to identify into which DOM object the View’s root element will be rendered. The second defines a common class we will use for the purpose of identifying these views to jQuery. Backbone automatically creates a new DIV object with the class ‘viewport’ when a view constructor is called. It will be our job to attach that DIV to the DOM. In the HTML, you will see the DIV\#main object where most of the work will be rendered.

<base view>+= (U->) [<-D->]
        initialize: function() {
            this.el = $(this.el);
            this.el.hide();
            this.parent.append(this.el);
            return this;
        },


The method above ensures that the element is rendered, but not visible, and contained within the DIV\#main. Note also that the element is not a sacrosanct object; the Backbone.View is more a collection of standards than a mechanism of enforcement, and so defining it from a raw DOM object to a jQuery object will not break anything.

Next, we will define the hide and show functions:

<base view>+= (U->) [<-D]
        hide: function() {
            if (this.el.is(":visible") === false) {
                return null;
            }
            promise = $.Deferred(_.bind(function(dfd) {
                this.el.fadeOut('fast', dfd.resolve)}, this));
            return promise.promise();
        },

        show: function() {
            if (this.el.is(':visible')) {
                return;
            }
            promise = $.Deferred(_.bind(function(dfd) {
                this.el.fadeIn('fast', dfd.resolve) }, this))
            return promise.promise();
        }
    });

Deferred is a new feature of jQuery. It is a different mechanism for invoking callbacks by attaching attributes and behavior to the callback function. By using this, we can say thing like “When everything is hidden (when every deferred returned from hide has been resolved), then show the appropriate viewport.” Deferreds are incredibly powerful, and this is a small taste of what can be done with them.

Before we move on, let’s take a look at the HTML we’re going to use for our one-page application:

<index.html>=
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title>
            The Backbone Store
        </title>
        <link rel="stylesheet" href="jsonstore.css" type="text/css">
        <product list template>
        <product detail template>
        <cart template>
    </head>
    <body>
        <div id="container">
            <div id="header">
                <h1>
                    The Backbone Store
                </h1>

                <div class="cart-info">
                </div>
            </div>

            <div id="main"> </div>
        </div>
        <script src="jquery-1.6.2.min.js" type="text/javascript"></script>
        <script src="underscore.js" type="text/javascript"></script>
        <script src="backbone.js" type="text/javascript"></script>
        <script src="store.js" type="text/javascript"></script>
    </body>
</html>

It’s not much to look at, but already you can see where that DIV\#main goes, as well as where we are putting our templates. The DIV\#main will host a number of viewports, only one of which will be visible at any given time.

Our first view is going to be the product list view, named, well, guess. Or just look down a few lines.

This gives us a chance to discuss one of the big confusions new Backbone users frequently have: What is render() for?. Render is not there to show or hide the view. Render() is there to change the view when the underlying data changes. It renders the data into a view. In our functionality, we use the parent class’s show() and hide() methods to actually show the view.

That call to \_super\_ is a Backbone idiom for calling a method on the parent object. It is, as far as anyone knows, the only way to invoke a superclass method if it has been redefined in a subclass. It is rather ugly, but useful.

<product list view>= (U->)
    var ProductListView = _BaseView.extend({
        id: 'productlistview',
        template: $("#store_index_template").html(),

        initialize: function(options) {
            this.constructor.__super__.initialize.apply(this, [options])
            this.collection.bind('reset', _.bind(this.render, this));
        },

        render: function() {
            this.el.html(_.template(this.template,
                                    {'products': this.collection.toJSON()}))
            return this;
        }
    });


That _.template() method is provided by undescore.js, and is a full-featured, javascript-based templating method. It’s not the fastest or the most feature-complete, but it is more than adequate for our purposes and it means we don’t have to import another library. It vaguely resembles ERB from Rails, so if you are familiar with that, you should understand this fairly easily.

And here is the HTML:

<product list template>= (<-U)
<script id="store_index_template" type="text/x-underscore-tmplate">
  <h1>Product Catalog</h1>
  <ul>
    <% for(i=0,l=products.length;i<l;++i) { p = products[i]; %>
    <li class="item">
      <div class="item-image">
        <a href="#item/<%= p.id %>">
          <img alt="<%= p.title %>" src="<%= p.image %>" />
        </a>
      </div>
      <div class="item-artist"><%= p.artist %></div>
      <div class="item-title"><%= p.title %></div>
      <div class="item-price">$<%= p.price %></div>
    </li>
    <% } %>
  </ul>
</script>


One of the most complicated objects in our ecosystem is the product view. It actually does something! The prefix ought to be familiar, but note that we are again using \#main as our target; we will be showing and hiding the various DIV objects in \#main again and again.

The only trickiness here is twofold: the (rather hideous) means by which one calls the method of a parnt class from a child class via Backbone’s class heirarchy (this is most definitely not Javascript standard), and keeping track of the itemcollection object, so we can add and change items as needed.

<product detail view>= (U->) [D->]
    var ProductView = _BaseView.extend({
        id: 'productitemview',
        template: $("#store_item_template").html(),
        initialize: function(options) {
            this.constructor.__super__.initialize.apply(this, [options])
            this.itemcollection = options.itemcollection;
            this.item = this.itemcollection.getOrCreateItemForProduct(this.model);
            return this;
        },


There are certain events in which we’re interested: keypresses and clicks on the update button and the quantity form. (Okay, “UQ” isn’t the best for “update quantity”. I admit that.) Note the peculiar syntax of “EVENT SELECTOR”: “methodByName” for each event.

Backbone tells us that the only events it can track by itself are those that jQuery’s “delegate” understands. As of 1.5, that seems to be just about all of them.

<product detail view>+= (U->) [<-D->]
        events: {
            "keypress .uqf" : "updateOnEnter",
            "click .uq"     : "update",
        },

And now we will deal with the update. This code ought to be fairly readable: the only specialness is that it’s receiving an event, and we’re “silencing” the call to cart.add(), which means that the cart collection will not publish any events. There are only events when the item has more than zero, and that gets called on cart_item.update().

In the original tutorial, this code had a lot of responsibility for managing the shopping cart, looking into it and seeing if it had an item for this product, and there was lots of accessing the model to get its id and so forth. All of that has been put into the shopping cart model, which is where it belongs: knowledge about items and each item’s relationship to its collection belongs in the collection.

Look closely at the update() method. The reference this.\$ is a special Backbone object that limits selectors to objects inside the element of the view. Without it, jQuery would have found the first input field of class ‘uqf’ in the DOM, not the one for this specific view. this.\$('.uqf') is shorthand for $('uqf', this.el), and helps clarify what it is you’re looking for.

 

<product detail view>+= (U->) [<-D->]
        update: function(e) {
            e.preventDefault();
            this.item.update(parseInt(this.$('.uqf').val()));
        },

        updateOnEnter: function(e) {
            if (e.keyCode == 13) {
                return this.update(e);
            }
        },


The render is straightforward:

<product detail view>+= (U->) [<-D]
        render: function() {
            this.el.html(_.template(this.template, this.model.toJSON()));
            return this;
        }
    });

The product detail template is fairly straightforward. There is no underscore magic because there are no loops.

<product detail template>= (<-U)
<script id="store_item_template" type="text/x-underscore-template">
  <div class="item-detail">
    <div class="item-image">
      <img alt="<%= title %>" src="<%= large_image %>" />
    </div>
  </div>
  <div class="item-info">
    <div class="item-artist"><%= artist %></div>
    <div class="item-title"><%= title %></div>
    <div class="item-price">$<%= price %></div>
    <form action="#/cart" method="post">
      <p>
        <label>Quantity:</label>
        <input class="uqf" name="quantity" size="2" type="text" value="1" />
      </p>
      <p>
        <input class="uq" type="submit" value="Add to Cart" />
      </p>
    </form>
    <div class="item-link">
      <a href="<%= url %>">Buy this item on Amazon</a>
    </div>
    <div class="back-link">
      <a href="#">&laquo; Back to Items</a>
    </div>
  </div>
</script>

So, let’s talk about that shopping cart thing. We’ve been making the point that when it changes, when you call item.update within the product detail view, any corresponding subscribing views sholud automatically update.

<cart widget>= (U->) [D->]
    var CartWidget = Backbone.View.extend({
        el: $('.cart-info'),
        template: $('#store_cart_template').html(),

        initialize: function() {
            this.collection.bind('change', _.bind(this.render, this));
        },


And there is the major magic. CartWidget will be initialized with the ItemCollection; when there is any change in the collection, the widget will receive the ‘change’ event, which will automatically trigger the call to the widget’s render() method.

The render method will refill that widget’s HTML with a re-rendered template with the new count and cost, and then wiggle it a little to show that it did changed:

<cart widget>+= (U->) [<-D]
        render: function() {
            this.el.html(
                _.template(this.template, {
                    'count': this.collection.getTotalCount(),
                    'cost': this.collection.getTotalCost()
                })).animate({paddingTop: '30px'})
                .animate({paddingTop: '10px'});
        }
    });

And the HTML for the template is dead simple:

<cart template>= (<-U)
<script id="store_cart_template" type="text/x-underscore-template">
  <p>Items: <%= count %> ($<%= cost %>)</p>
</script>


Lastly, there is the Router. In Backbone, the Router is a specialized View for invoking other views. It listens for one specific event: when the window.location.hash object, the part of the URL after the hash symbol, changes. When the hash changes, the Router invokes an event handler. The Router, since its purpose is to control the major components of the one-page display, is also a good place to keep all the major components of the sytem. We’ll keep track of the Views, the ProductCollection, and the ItemCollection.

<router>= (U->) [D->]
    var BackboneStore = Backbone.Router.extend({
        views: {},
        products: null,
        cart: null,

There are two events we care about: view the list, and view a detail. They are routed like this:

<router>+= (U->) [<-D->]
        routes: {
            "": "index",
            "item/:id": "product",
        },

Like most Backbone objects, the Router has an initialization feature. I create a new, empty shopping cart and corresponding cart widget, which doesn’t render because it’s empty. I then create a new ProductCollection and and corresponding ProductListView. These are all processes that happen immediately.

What does not happen immediately is the fetch() of data from the back-end server. For that, I use the jQuery deferred again, because fetch() ultimately returns the results of sync(), which returns the result of an ajax() call, which is a deferred.

<router>+= (U->) [<-D->]
        initialize: function(data) {
            this.cart = new ItemCollection();
            new CartWidget({collection: this.cart});

            this.products = new ProductCollection([], {
                url: 'data/items.json'});
            this.views = {
                '_index': new ProductListView({
                    collection: this.products
                })
            };
            $.when(this.products.fetch({reset: true}))
                .then(function() { window.location.hash = ''; });
            return this;
        },


There are two things to route to, but we must also route from. Remember that our two major views, the product list and the product detail, inherited from \_BaseView, which has the hide() and show() methods. We want to hide all the views, then show the one invoked. First, let’s hide every view we know about. hide() returns either a deferred (if the object is being hidden) or null. The _.select() call at the end means that this method returns only an array of deferreds.

<router>+= (U->) [<-D->]
        hideAllViews: function () {
            return _.select(
                _.map(this.views, function(v) { return v.hide(); }),
                function (t) { return t != null });
        },

Showing the product list view is basically hiding everything, then showing the index:

<router>+= (U->) [<-D->]
        index: function() {
            var view = this.views['_index'];
            $.when(this.hideAllViews()).then(
                function() { return view.show(); });
        },


On the other hand, showing the product detail page is a bit trickier. In order to avoid re-rendering all the time, I am going to create a view for every product in which the user shows interest, and keep it around, showing it a second time if the user wants to see it a second time.

Not that we pass it the ItemCollection instance. It uses this to create a new item, which (if you recall from our discussion of getOrCreateItemForProduct()) is automagically put into the collection as needed. Which means all we need to do is update this item and the item collection changes, which in turn causes the CartWidget to update automagically as well.

<router>+= (U->) [<-D]
        product: function(id) {
            var product, v, view;
            product = this.products.detect(function(p) { return p.get('id') == (id); })
            view = ((v = this.views)['item.' + id]) || (v['item.' + id] = (
                new ProductView({model: product,
                                 itemcollection: this.cart}).render()));
            $.when(this.hideAllViews()).then(
                function() { view.show(); });
        }
    });


Finally, we need to start the program

<initialization>= (U->)
    $(document).ready(function() {
        new BackboneStore();
        Backbone.history.start();
    });

 

The Program

Here’s the entirety of the program:

<store.js>=
(function() {

<product models>

<cart models>

<base view>

<product list view>

<product detail view>

<cart widget>

<router>

<initialization>

}).call(this);

And that’s it. Put it all together, and you’ve got yourself a working Backbone Store.

This code is available at my github at The Backbone Store.

03Feb

Backbone Store v 0.2 released

Posted by Elf Sternberg as javascript, programming

The last version of the Backbone Store was pretty sloppy. I’ve cleaned it up, with some serious revisions to both the code and the commentary, and you’re welcome to look it over. As always, it’s available from my Github.

Note: This tutorial is out of date! There are now two updates: A Javascript, HTML, and CSS Version, and a Coffeescript, HAML, and Stylus Version.

Introduction

I’ve been playing with Backbone.js, a small but nifty Javascript library that provides a small Model-View-Controller framework where Models can generate events that trigger View changes, and vice versa, along with a Collections models so groups of models can cause view-level events, and a Sync library that provides a basic REST architecture for propagating client-made changes back to the server.

There are a number of good tutorials for Backbone, such as: Meta Cloud, &Yet’s Tutorial, Backbone Mobile (which is written in Coffee), and Backbone and Django. However, a couple of months ago I was attempting to learn Sammy.js, a library very similar to Backbone, and they had a nifty tutorial called The JsonStore.

In the spirit of The JSON Store, I present The Backbone Store.

Literate Program

A note: this article was written with the Literate Programming toolkit Noweb. Where you see something that looks like <this>, it’s a placeholder for code defined elsewhere in the document. Placeholders with an equal sign at the end of them indicate the place where that code is defined. The link (U->) indicates that the code you’re seeing is defined here but used later in the document, and (<-U) indicates it was used earlier but is being defined here.

The Store

The store has three features: A list of products, a product detail page, and a “shopping cart” that does nothing but tally up the number of products total that you might wish to order. The main viewport flips between a list of products and a product detail; the shopping cart quantity tally is always visible.

Let’s start by showing you the HTML that we’re going to be exploiting. As you can see, the shopping cart’s primary display is already present, with zero items showing. DOM ID “main” is empty. We’ll fill it with templated data later.

HTML

<index.html>=
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

        <title>The Backbone Store</title>
        <link rel="stylesheet" href="jsonstore.css" type="text/css" media="screen" charset="utf-8" />

        <product list template>

        <product template>

    </head>
    <body>
        <div id="container">
            <div id="header">
                <h1>
                    The Backbone Store
                </h1>

                <div class="cart-info">
                    My Cart (<span class="cart-items">0</span> items)
                </div>
            </div>

            <div id="main">
            </div>
        </div>
        <script src="jquery-1.4.4.min.js" type="text/javascript"></script>
        <script src="jquery.tmpl.min.js" type="text/javascript"></script>
        <script src="underscore.js" type="text/javascript"></script>
        <script src="backbone.js" type="text/javascript"></script>
        <script src="store.js" type="text/javascript"></script>
    </body>
</html>

This is taken, more or less, straight from The JSON Store. I’ve included one extra thing, aside from jQuery and Backbone, and that’s the jQuery Templates kit. There is also a simplified JSON file that comes in the download; it contains six record albums that the store sells. (Unlike the JSON store, these albums don’t exist; the covers were generated during a round of The Album Cover Game.)

The Program

And here’s the skeleton of the program we’re going to be writing:

<store.js>=
<product models>

<product list view>

<shopping cart models>

<shopping cart view>

<product view>

<application>

Products and Product List View

To start, I have a list of products. The basic product is just a model, with nothing to show for it; the list of products is a Backbone.Collection, with one feature, the comparator, which sorts the albums in order by album title.

<product models>= (<-U)
var Product = Backbone.Model.extend({});

var ProductCollection = Backbone.Collection.extend({
    model: Product,
    comparator: function(item) {
        return item.get('title');
    }
});

The ProductCollection is what we want to show when the user isn’t looking at a specific product. I create a Backbone.View object class ProductListView:

<product list view>= (<-U)
var ProductListView = Backbone.View.extend({
    el: $('#main'),
    indexTemplate: $("#indexTmpl").template(),

    render: function() {
        var sg = this;
        this.el.fadeOut('fast', function() {
            sg.el.empty();
            $.tmpl(sg.indexTemplate, sg.model.toArray()).appendTo(sg.el);
            sg.el.fadeIn('fast');
        });
        return this;
    }

});

Here, we’ve told this view that it’s principle element is the DOM ID “main”, allocated an indexTemplate using the jQuery template complire, and created a render function that fades out “main”, replaces its content with a rendered template, and fades it back in.

The template looks like this:

<product list template>= (<-U)
        <script id="indexTmpl" type="text/x-jquery-tmpl">
        <div class="item">
         <div class="item-image">
             <a href="#item/${cid}"><img src="${attributes.image}" alt="${attributes.title}" /></a>
         </div>
         <div class="item-artist">${attributes.artist}</div>
            <div class="item-title">${attributes.title}</div>
            <div class="item-price">$${attributes.price}</div>
        </div>
        </script>

There’s some Demeter violations going on here, in that I have to know about the attributes of a Backbone model, something that’s normally hidden within the class. But this is good enough for our purposes. The above is a jQuery template, and the \$\{\} syntax is what’s used to dereference variables within a template.

(As an aside, I think that the set and get methods of Backbone.Model are a poor access mechanism. I understand why they’re there, and I can only hope that someday Javascript Getter and Setters become so well-established as to make set and get irrelevant.)

The Shopping Cart

Before I move on to the product view, I want to go over the shopping cart.

A little rocket science here: A Cart contains CartItems. Each “item” represents a quantity of a Product. (I know, that always struck me as odd, but that’s how most online stores do it.) CartItem has an update method that allows you to add more (but not remove any– hey, the Sammy store wasn’t any smarter, and this is For Demonstration Purposes Only), and we use the set method to make sure that a “change” event is triggered.

The Cart, in turn, has a method, getByPid (“Product ID”), which is meant to assist other objects in finding the CartItem associated with a specific product. Here, I’m just using the Backbone default client id. Once we’ve found that item, we can just call update() on it.

<shopping cart models>= (<-U)
var CartItem = Backbone.Model.extend({
    update: function(amount) {
        this.set({'quantity': this.get('quantity') + amount});
    }
});

var Cart = Backbone.Collection.extend({
    model: CartItem,
    getByPid: function(pid) {
        return this.detect(function(obj) { return (obj.get('product').cid == pid); });
    },
});

The cart is represented by a little tag in the upper right-hand corner of the view; it never goes away, and its count is always the total number of Products (not CartItems) ordered. So the CartView needs to update whenever a CartItem is added or updated. And we want a nifty little animation to go with it:

<shopping cart view>= (<-U)
var CartView = Backbone.View.extend({
    el: $('.cart-info'),

    initialize: function() {
        this.model.bind('change', _.bind(this.render, this));
    },

    render: function() {
        var sum = this.model.reduce(function(m, n) { return m + n.get('quantity'); }, 0);
        this.el
            .find('.cart-items').text(sum).end()
            .animate({paddingTop: '30px'})
            .animate({paddingTop: '10px'});
    }
});

A couple of things here: the render is rebound to this to make sure it renders in the context of the view. I found that that was not always happening. Note the use of reduce, a nifty method from underscore.js that allows you to build a result out an array using an anonymous function. This reduce, obviously, sums up the total quantity of items in the cart. Also, jQuery enthusiasts could learn (I certainly did!) from the .find() and .end() methods, which push a child object onto the stack to be modified, and then pop it off after the operation has been applied.

One of the big things this illustrates is that a Backbone.View is not a full-page event; it’s a mini-application for drawing its own little universe, that may be part of a larger universe. It’s entirely possible to have lots of Views on a page.

Also, this cart does not have a template associated with it: we’re changing a single textual item on the page and animating another one that is always present.

The Product Detail View

So now we’re down to the ProductView. This is slightly more complicated. First, let me show you a skeleton of the view, similar to the one we saw for the ProductListView:

<product view>= (<-U)
var ProductView = Backbone.View.extend({
    el: $('#main'),
    itemTemplate: $("#itemTmpl").template(),

<product events>

<product view initialization>

<update product>

<render product>
});

The reason the ProductView is complex is because it’s going to interact with the shopping cart. We need to keep track of the cart. There are two ways of dealing with this: Have the ProductView track down its cart item every time, or keep a reference to an individual track item having found it once. I’m going with the first option.

<product view initialization>= (<-U)
    initialize: function(options) {
        this.cart = options.cart;
    },

Rendering is exactly the same as that for the ProductListView. In fact, it’s so similar, I’m thinking maybe I should have made this an abstract function and mixed it in afteward:

<render product>= (<-U)
    render: function() {
        var sg = this;
        this.el.fadeOut('fast', function() {
            sg.el.empty();
            $.tmpl(sg.itemTemplate, sg.model).appendTo(sg.el);
            sg.el.fadeIn('fast');
        });
        return this;
    }

The template for a ProductView, however, has some interesting qualities:

<product template>= (<-U)
        <script id="itemTmpl" type="text/x-jquery-tmpl">
        <div class="item-detail">
          <div class="item-image"><img src="${attributes.large_image}" alt="${attributes.title}" /></div>
          <div class="item-info">
            <div class="item-artist">${attributes.artist}</div>
            <div class="item-title">${attributes.title}</div>
            <div class="item-price">$${attributes.price}</div>
            <div class="item-form">
              <form action="#/cart" method="post">
                <input type="hidden" name="item_id" value="${cid}" />
                <p>
                  <label>Quantity:</label>
                  <input type="text" size="2" name="quantity" value="1" class="uqf" />
                </p>
                <p><input type="submit" value="Add to Cart" class="uq" /></p>
              </form>
            </div>
            <div class="item-link"><a href="${attributes.url}">Buy this item on Amazon</a></div>
            <div class="back-link"><a href="#">&laquo; Back to Items</a></div>
          </div>
        </div>
        </script>

Note the octothorpe used as the target link for “Home”. I kept thinking an empty link or just “/” would be appropriate, but no, it’s an octothorpe.

Also note that it has a form. (Again, note the Demeter violations.) What we want is to update the shopping cart whenever the user enters a number into the input box and either presses “Add To Cart” or the ENTER button. That gives us our methods: We’re in a view for a specific product; we must see if the customer has a CartItem for that product in the Cart, and add or update it as needed. Like so:

<update product>= (<-U)
    update: function(e) {
        e.preventDefault();
        var cart_item = this.cart.getByPid(this.model.cid);
        if (_.isUndefined(cart_item)) {
            cart_item = new CartItem({product: this.model, quantity: 0});
            this.cart.add(cart_item, {silent: true});
        }
        cart_item.update(parseInt($('.uqf').val()));
    },

    updateOnEnter: function(e) {
        if (e.keyCode == 13) {
            return this.update(e);
        }
    },

But how to do these events get triggered? Go back to the ProductView skeleton above; there’s a placeholder for “product events”, which looks like this:

<product events>= (<-U)
    events: {
        "keypress .uqf" : "updateOnEnter",
        "click .uq"     : "update",
    },

Backbone uses a curious definition of an event with an “event selector”, followed by a target method of the View class. Backbone is also limited about what events can be used here, as the following events cannot be wrapped by jQuery’s delegate method and do not work: “focus”, “blur”, “change”, “submit”, and “reset”.

We preventDefault to keep the traditional meaning of the submit button from triggering. When the CartItem is updated, it triggers a “change” event, and the CartView will update itself automatically. I added the “silent” option to keep the “change” event from triggering twice when adding a new CartItem to the Cart.

The Router

The router is a fairly straightforward component. It’s purpose is to pay attention to the “#hash” portion of your URL and, when it changes, do something. Anything, really. Backbone.History is the event listener for the hash, so it has to be activated after the application. In many ways, a Backbone “Controller” is just a big View with authority over the entire Viewport.

Here’s the skeleton of our router, along with its instantiation:

<application>= (<-U)
var Workspace = Backbone.Controller.extend({
<application variables>

<routes>

<initialization>

<index render call>

<product render call>
});

workspace = new Workspace();
Backbone.history.start();

There are two routes that we want to present: the index (our list of products) and the item (a product detail). So, using Backbone.Controller, we’re going to route the following:

<routes>= (<-U)
    routes: {
        "": "index",
        "item/:id": "item",
    },

There are a few things I want to track: the index view, the individual product views, and the shopping cart.

<application variables>= (<-U)
    _index: null,
    _products: null,
    _cart :null,

Now, we can render the index view:

<index render call>= (<-U)
    index: function() {
        this._index.render();
    },

There are two things left in our workspace, that we haven’t defined. The intialization, and the product render.

Initialization consists of getting our product list, creating a shopping cart to hold “desired” products (and in quantity!), and creating the product list view.

<initialization>= (<-U)
    initialize: function() {
        var ws = this;
        if (this._index === null) {
            $.ajax({
                url: 'data/items.json',
                dataType: 'json',
                data: {},
                success: function(data) {
                    ws._cart = new Cart();
                    new CartView({model: ws._cart});
                    ws._products = new ProductCollection(data);
                    ws._index = new ProductListView({model: ws._products});
                    Backbone.history.loadUrl();
                }
            });
            return this;
        }
        return this;
    },

Here, I load the data, and upon success create a new ProductCollection from the data, a new shopping cart, and a new ProductListView for the product collection. I then call Backbone.history.loadUrl(), which then routes us to the correct view.

Thanks to this, users can bookmark places in your site other than the home page. Yes, the bookmark will be funny and have at least one octothorpe in it, but it will work.

And now I’m down to one last thing. I haven’t defined that product render call in the application controller. The one thing I don’t want to do is have ProductViews for every product, if I don’t need them. So I want to build them as-needed, but keep them, and associate them with the local Product, so they can be recalled whenever we want. The underscore function isUndefined is excellent for this.

<product render call>= (<-U)
    item: function(id) {
        if (_.isUndefined(this._products.getByCid(id)._view)) {
            this._products.getByCid(id)._view = new ProductView({model: this._products.getByCid(id),
                                                                 cart: this._cart});
        }
        this._products.getByCid(id)._view.render();
    }

And that’s it. Put it all together, and you’ve got yourself a working Backbone Store.

This code is available at my github at The Backbone Store.

Index of code references:

Postscript:  Someone pointed out to me that using the toJSON() method that Backbone provides for models and collections would provide better results than the Demeter violations I complained about.  The problem with that is that I’m using the CID as my primary key, and toJSON doesn’t include the CID in the JSON object.  I suppose I could override the toJSON method and add it, or write an inline to decorate the JSON product with the CID, but after all that it doesn’t seem worth the effort.  For more canonical work, though, it’s something to keep in mind.

I’ve just spent the past four days working on a “homework assignment,” a coding assignment given to me by ${MEGACORP} to assess my skills as a web developer and programmer. I was sent a zip file with a README in it and a pair of sub folders: one of the server, one of the client, and told to build it out and “make it work.”

It was broken as delivered. The install worked fine, but I winced when I realized that yarn had installed 2,572 different packages in the library trees, including the entire flow binary and the OCaml runtime that comes with it! Running the server threw a dozen errors, and the client showed only a blank white page with a red notification bar, completely unpopulated.

I was coming out of Splunk, where I’d been working with Splunk’s own server and client foundation for five years. That foundation was built on top of Django and Backbone, two skills I clearly had when Splunk hired me in 2013. In 2015, some of our teams had started to work with React, and I’d been fortunate enough to be part of that.

But the 2017-2018 development cycle had been all Go, all the time. I was woefully out of practice as a web developer.

Here’s what they threw at me:

GraphQL

GraphQL is a query language specified by Facebook that handles client/server transactions. You specify the possibly deeply nested data you want sent to the client, and when you make the request you get a JSON object pre-populated with that nested data. Instead of assembling all the data out of different requests, your client sends a single request and gets back all the data it needs to fill in a page.

It’s a pretty good idea. But it was new to me.

Yoga

Yoga is a proxy server into which you define the GraphQL endpoints for your client. You write your specification here, defining the structure of the data and the handlers for assembling that data out of your back-end. Also completely new to me. You write in Javascript or one of its derivatives (Typescript, Clojurescript).

Prisma

Prisma is yet another proxy server, this one written in Scala. Prisma is the big aggregate engine; it talks to all your back-end services and assembles the data for you.

The relationship between Yoga and Prisma is basically something of a firewall; Yoga limits the kinds of transactions that can happen, and has the capability to restrict access based on authentication issues. Prisma will take whatever Yoga sends it and do the heavy lifting of assembling and aggregating the objects to send to the client.

JWT

JavascriptWebTokens were being used for authentication. I straight up admit that I ended up using them badly and contrary to security advice. We hadn’t used them at Splunk until the “cloud era,” and by then I was working on Kubernetes deployment issues and didn’t get to learn much about them. They’re basically cryptographically signed bundles that describe “claims,” a word that encompasses authentication and authorization, but they’re short so they fit in the HTTP Authentication: header. Unfortunately, they’re also blatant, so if you’re using them in an unsecured environment anyone can grab them and use them against you.

ES6 Javascript

Oh, and did I mention that this was entirely ES6 Javascript, which we hadn’t been using at Splunk at all? So I got to learn a whole bunch of new things! And actually, I was pretty stoked about that. It has real Classes now, and the function shorthand operator with no this overrides (inhering the lexical this is awesome!), and it does destructuring and lexical naming and a whole bunch of stuff that I enjoyed the hell out of it.

React

I wasn’t new to React. In fact, I had a pretty solid working relationship with React, and even had a good idea of how to hack it to use with older, Backbone-based data sources. But that was two and a half years ago, and React has moved. There are more libraries today.

Apollo

Apollo is a toolkit for GraphQL clients. The Apollo toolkit creates React Contexts in which you can write GraphQL queries and get them back as processed objects ready to feed directly into your React components and draw them at will. Apollo was interesting, but I found its relationship with React a bit awkward, especially when trying to handle post-event routing. Apollo wants to deliver you ultimately render, but in some cases, such as log-in, what you want is both the render event and some other behavior, such as dismissing the login pop-up. That was tricky.

Material & Material UI

Material is Google’s own CSS Design System, the one we’ve all come to recognize. It has paper and cards as metaphors, and has a whole bunch of guidelines for building out the best UIs. It’s fine as far as design systems go, and it’s certainly mature.

Material UI is an implementation of Material in React; it provides a whole bunch of widgets for all the things one might normally do with an application and makes them easy to bring to life.

… and that was just the basics. I had to learn all of those enough to do the job, and this was for a code quiz. It was like a 32-hour whiteboarding session without any of the “cultural fit” stuff going on. It was grueling, and I hope I don’t have to do anything like it very soon.

Part 2: What I actually did on the project.

So, today I did I thing I’ve never done before. I quit.

In all my career as a software developer, I’ve never quit from a position like this. In college I quit a few jobs that weren’t software development, such as the warehouse job, the data entry position, and the pizza delivery service. I’ve quit a few small start-ups that weren’t paying me what I was worth, but then at the time nobody was getting paid what they were theoretically worth, and every single one of those start-ups was still incredibly valuable: they let me keep my resume alive, and they let me learn useful skills. But all the big job endings, from CompuServe, F5, and Isilon, had been either shutdowns or layoffs. CompuServe was just axed by some holding company owned by AOL. F5 and Isilon let me go during the massive layoffs of the Big Tech Bubble and the Great Recession, respectively.

I’ve never just… left. Certainly not when the opportunities at the company were pretty good. At Isilon I was definitely going stale, but the same can’t be said for F5 or CompuServe. At those, I’d been learning great things up until the very end. At F5 we’d had a great ANTLR project underway, and at CompuServe I’d been knee-deep into making contributions to the Python standard library and maintaining the original mod-logging library documentation for Apache.

Today, I quit because I’d lost my niche.

Today I left my job a Splunk for no other reason than that I wanted to. I’d been learning a fantastic array of new stuff. I had Kubernetes deployments working reliably, was deploying them on AWS, had transitioned away from Ansible onto something using Terraform and Ksonnet, was writing a Kubernetes-based microservice in Golang to gather billing information for tenanted systems, doing internal logging and using the Prometheus client libraries for observability (but actually scraping the data into Splunk instead, which was hella fun), and had a Grafana-to-Splunk dashboard transformer kinda sorta working. Codeship, Gitlab, Jenkins, Swagger, Docker, Make, Go, you name it, I’m doing it. And a lot of Python, too, as it remains the official language of Splunk Enterprise Engineering. Oh, and the front-end of our proxy is written in React, too, which they gave to me because “You’re a UI guy, right?”

I was a UI guy. In fact, that was why Splunk hired me in the first place. I’d literally written the book on writing SPAs (Single Page Applications) using Backbone, JQuery, and REST, after learning how to do that at a previous start-up. I had a niche as an expert single-page-application developer using Python middle tiers and SQL back-ends. I could, in a pinch, even do it in Ruby on Rails, but it would take a week, whereas if you gave me Django and Postgresql I would have it up and running in an hour.

I had an elevator pitch. You can still read it. I was someone who could do the whole stack, even on AWS, well enough to get your start-up off the ground. I did it five times: two failures, two lifestyle successes, and one runaway success that allowed me to survive the 2008 recession, paying for Omaha’s medicines and the kids’ schooling, without having to tighten my belt too much.

Looking at the list of skills I’ve got from Splunk, I can’t say that I have an elevator pitch anymore. Everything I’ve been doing for the past six months has been too fast, too new, too much all at once for me to develop a sense of expertise in anything— well, except for writing a Prometheus-instrumented microservice in Go. Maybe that’s not a bad niche, since the bulk of software development is just plumbing anyway these days, a paint-by-numbers process of input, map, filter, output. The problem is that anyone can do plumbing. I like to think I’m better at the really hard problem in that area of development than most people– but then I’m reminded that most people don’t care about readable code anymore. It’s all replaceable anyway.

Part of this coming year will be to figure out what my next elevator pitch will be, and if there’s a place in the industry where I want to pitch it. I was good at industrial websites, and I was good at server design, and I was good at UIs for embedded systems. The question is: what will I be good at in 2020? What will I need to be good at in 2020?

I guess I’m about to find out.

So, Spectrum IEEE has a “The sky is falling! The sky is falling!” article claiming that in 2016 tech layoffs have been nasty and that in 2017 it’s going to get even nastier. This is one of many articles on this theme, but it’s a little disheartening to see it in Spectrum. Worse, none of the articles I’ve read on this theme list the skills are going to be out-of-date. Which skills? What disciplines?

In 2008, I was laid off after 8 years at a large company, and I’d been using the same tools for those 8 years. As a front-end developer for dev-ops shops, my skills were woefully out-of-date: We’d been using Sencha (JS) and Webware (PY), with some Python 2 Python-to-C libraries. I knew nothing about what the cool kids were doing. I sat down and in a few days taught myself Django and jQuery; I rebooted by SQL knowledge from my 90s-era experience with Oracle and taught myself the ins and outs of Postgresql.

And then, in the bottom of the recession, I took shit contracts that paid very little (in one mistake, nothing) but promised to teach me something. I worked for a Netflix clone startup; I traded my knowledge of video transcoding for the promise of learning AWS. I worked for a genetic engineering startup, trading my knowledge of C++ for the promise of learning Node, Backbone, SMS messaging, and credit card processing; a textbook startup, trading my knowledge of LaTeX for the promise of learning Java; an advertising startup trading my basic Django skills to learn modern unit testing; a security training startup, trading my knowledge of assembly language in order to learn Websockets.

The market improved. I never stopped learning. I gave speeches at Javascript and Python meet-ups. Recruiters sought me out. I’ve been at another big company for four years now.

Will things go to hell in March? I don’t care. I have the one skill that matters.

Subscribe to Feed

Categories

Calendar

November 2019
M T W T F S S
« Oct    
 123
45678910
11121314151617
18192021222324
252627282930