Jul
24

Backbone.Spark: Ember style computed properties for Backbone.js

posted on 24 July 2012 in programming

Warning: Please consider that this post is over 11 years old and the content may no longer be relevant.

Backbone.Spark provides computed property support for Backbone.js, the goal of the extension is to make computed properties behave in the same way as a normal attribute so your other code doesn’t need to know the difference.

The latest version of Backbone.Spark and examples can be found at GitHub

Why would you use a computed property?

Say you were working with files, and your file model has the full path to the file, e.g. C:\Users\joe\Documents\thefile.txt. Now you want to display only the file name in a templated view, how would you do this?

You could only store the file path attribute in the model and have the view extract the file name, but what happens when you have mutliple views pointing to the model, you’ll have have to replicate the code to compute the file name, not very DRY.

Another way you could approach it is to have a file name attribute of the model and watch for changes to the file path, when it changes you update the value of file name. This works but it relies on good code to keep the dependent file path attribute in sync, and as you add more dependent values, say file extension, this code can bloat out. You’re also running code to compute the values of the dependent attributes that may never be used.

Backbone.Spark provides an alternative, allowing you to specify a the file name as a function, so it gets computed from the file path every time it’s requested and you’re guaranteed to have the correct value.

But Backbone.Mutators already exists, why wouldn’t I just use this?

Right you are, and full credit to Backbone.Mutators, it’s a great extension and Backbone.Spark is based on it. My problem with mutators is that other code needs to know the dependencies for the mutator. If you want to update the view when file name changes, you need to watch for a change to the file path attribute. Backbone.Spark overcomes this by raising change events whenever any of the dependent properties change.

Enough of the talk, show me some code

Backbone.Spark defines a new Model class that you can choose to inherit from.

Basic usage

To use, declare a new property object called sparks and add function properties for the ‘getter’ accessors.
Call dependsOn with the spark to specify which attributes this spark depends on. Whenever the dependent attribute(s) are changed, a change event will also be triggered for this spark.

App.File = Backbone.Spark.Model.extend({

    defaults: function() {
      return {
        content: null,
        filePath: null
      };
    },

    sparks: {

        extn: function() {
            var i = this.filePath.lastIndexOf('.');
            return i >= 0 ? this.filePath.substr(i) : '';
        }.dependsOn('filePath')

    }

});

Using setters

When calling set on a spark, the set function arguments are passed to the spark function.
You can identify a getter call by testing arguments.length === 0, a setter call has the following arguments:

key: The name of the spark property.
value: The new value to be set.
options: Any options passed to the set call, including whether this call should raise events or not (silent).
set: The base class set function.

App.File = Backbone.Spark.Model.extend({

    defaults: function() {
      return {
        content: null,
        filePath: null
      };
    },

    sparks: {

        extn: function(key, value, options, set) {
            if (arguments.length === 0) {
                var i = this.filePath.lastIndexOf('.');
                return i >= 0 ? this.filePath.substr(i) : '';
            } else {
                var filePath = this.get('filePath'),
                    i = filePath.lastIndexOf('.');

                if (i >= 0)
                    this.set('filePath', filePath.substr(0, i) + value);
            }
        }.dependsOn('filePath')

    }

});

Using

Backbone.Spark is released under the Apache License, Version 2.0.

Get on GitHub