Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
159 lines (113 sloc) 4.56 KB


A plugin to make Backbone.js keep track of nested attributes. Download minified version.

The Need

Suppose you have a Backbone Model with nested attributes, perhaps to remain consistent with your document-oriented database. Updating the nested attribute won't cause the model's "change" event to fire, which is confusing.

var user = new Backbone.Model({
  name: {
    first: 'Aidan',
    last: 'Feldman'

user.bind('change', function(){
  // this is never reached!

user.get('name').first = 'Bob';;

Wouldn't it be awesome if you could do this?

user.bind('change:name.first', function(){ ... });


  1. Download the latest version here, and add backbone-nested.js to your HTML <head>, after backbone.js is included (tested against jQuery v1.7.1, Underscore v1.3.1 and Backbone v0.5.3).

    <script type="text/javascript" src=""></script>
    <script type="text/javascript" src="underscore.js"></script>
    <script type="text/javascript" src="backbone.js"></script>
    <script type="text/javascript" src="backbone-nested.js"></script>
  2. Change your models to extend from Backbone.NestedModel, e.g.

    var Person = Backbone.Model.extend({ ... });
    // becomes
    var Person = Backbone.NestedModel.extend({ ... });
  3. Change your getters and setters to not access nested attributes directly, e.g.

    user.get('name').first = 'Bob';
    // becomes
    user.set({'name.first': 'Bob'});

Best of all, Backbone.NestedModel is designed to be a backwards-compatible, drop-in replacement of Backbone.Model, so the switch can be made painlessly.

Nested Attributes

get() and set() will work as before, but nested attributes should be accessed using the Backbone-Nested string syntax:


// dot syntax
  'name.first': 'Bob',
  'name.middle.initial': 'H'
user.get('name.first') // returns 'Bob'
user.get('name.middle.initial') // returns 'H'

// object syntax
  'name': {
    first: 'Barack',
    last: 'Obama'


// object syntax
  'addresses': [
    {city: 'Brooklyn', state: 'NY'},
    {city: 'Oak Park', state: 'IL'}
user.get('addresses[0].state') // returns 'NY'

// square bracket syntax
  'addresses[1].state': 'MI'



"change" events can be bound to nested attributes in the same way, and changing nested attributes will fire up the chain:

// all of these will fire when 'name.middle.initial' is set or changed
user.bind('change', function(model, newVal){ ... });
user.bind('change:name', function(model, newName){ ... });
user.bind('change:name.middle', function(model, newMiddleName){ ... });
user.bind('change:name.middle.initial', function(model, newInitial){ ... });

// all of these will fire when the first address is added or changed
user.bind('change', function(model, newVal){ ... });
user.bind('change:addresses', function(model, addrs){ ... });
user.bind('change:addresses[0]', function(model, newAddr){ ... });
user.bind('change:addresses[0].city', function(model, newCity){ ... });

"add" and "remove"

Additionally, nested arrays fire "add" and "remove" events:

user.bind('add:addresses', function(model, newAddr){ ... });
user.bind('remove:addresses', function(model, oldAddr){ ... });

Special Methods


Acts like unset(), but if the unset item is an element in a nested array, the array will be compacted. For example:

user.get('addresses').length; //=> 2
user.get('addresses').length; //=> 1


Accessing a nested attribute will throw a warning in your console, because it's safer to use the getter syntax above. To silence these warnings, add an argument of {silent: true} to get():

user.get('addresses[0]'); // gives a warning in your console
user.get('addresses[0]', {silent:true}); // (silent)


Pull requests are more than welcome - please add tests, which can be run by opening test/index.html.