A plugin to make Backbone.js keep track of nested attributes. Download minified version.
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';
user.save();
Wouldn't it be awesome if you could do this?
user.bind('change:name.first', function(){ ... });
-
Download the latest version here, and add
backbone-nested.js
to your HTML<head>
, afterbackbone.js
is included (tested against jQuery v1.7.2, Underscore v1.3.3 and Backbone v0.9.2).<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></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>
-
Change your models to extend from
Backbone.NestedModel
, e.g.var Person = Backbone.Model.extend({ ... }); // becomes var Person = Backbone.NestedModel.extend({ ... });
-
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.
get()
and set()
will work as before, but nested attributes should be accessed using the Backbone-Nested string syntax:
// dot syntax
user.set({
'name.first': 'Bob',
'name.middle.initial': 'H'
});
user.get('name.first') // returns 'Bob'
user.get('name.middle.initial') // returns 'H'
// object syntax
user.set({
'name': {
first: 'Barack',
last: 'Obama'
}
});
// object syntax
user.set({
'addresses': [
{city: 'Brooklyn', state: 'NY'},
{city: 'Oak Park', state: 'IL'}
]
});
user.get('addresses[0].state') // returns 'NY'
// square bracket syntax
user.set({
'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){ ... });
Additionally, nested arrays fire "add"
and "remove"
events:
user.bind('add:addresses', function(model, newAddr){ ... });
user.bind('remove:addresses', function(model, oldAddr){ ... });
Acts like set()
, but appends the item to the nested array. For example:
user.get('addresses').length; //=> 2
user.add('addresses', {
city: 'Seattle',
state: 'WA'
});
user.get('addresses').length; //=> 3
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.remove('addresses[0]');
user.get('addresses').length; //=> 1
HEAD (diff)
- fix
remove()
not firing'remove'
event when last element of array is removed (thanks @Kmandr) - fix
clear()
and set nested attributes onchangedAttributes()
(thanks @isakb) 'change'
events will no longer fire if new value matches the old
1.1.2 (diff)
changedAttributes()
should include the nested attribute paths- remove warnings when retrieving nested objects - more of a nuisance than a convenience
1.1.1 (diff)
- fixed
remove()
to not insert array back into itself - upgraded test suite to Backbone 0.9.2
1.1.0 (diff)
- Backbone 0.9.1 compatibiity
- fire 'remove' event from remove()
- added add() method
- added demo pages
1.0.3 (diff)
- fixed
toJSON()
(p3drosola)
1.0.2 (diff)
- added option to silence
get()
warnings for non-leaf attributes
1.0.1 (diff)
- header and documentation fixes
Initial release!
Pull requests are more than welcome - please add tests, which can be run by opening test/index.html. They can also be run from the command-line (requires PhantomJS):
$ npm install
$ grunt