backbone.supermodel v1.2.0
Backbone.SuperModel
Super as in fashion supermodel, not supernatural power. SuperModel extend Backbone.Model directly and override several of Backbone.Model methods to add support for nested attributes and many other features (coming soon!)
Install
Using NPM
npm install backbone.supermodelUsing bower
bower install backbone.supermodelUsing AMD script loader
- Copy
build/backbone.supermodel.amd.jsto your project - Load it with
require(['path/to/backbone.supermodel.amd'], function(SuperModel){})and make sure that your haveunderscoreandbackbonestup correctly
Use it the old fashion way
- Load
build/backbone.supermodel.jsin<head>or at the end of<body> - Make sure that you have
underscoreandbackboneloaded before that
Nested attribute
It is quite often that nested attributes are used in an application. Considering this object structure
var wallet = {
'money': {
'amount': 4000,
'currency': 'euro'
},
'name': 'Tan Nguyen'
}With Backbone.Model we can have a model with an attribute wallet to store the above information, like this
var myStuff = new Backbone.Model();
myStuff.set('wallet', wallet);
myStuff.get('wallet').money.amount; // 4000Although there is nothing wrong with this approach, it has several side-effects
By default an object is passed by reference and any changes to the object will affect all other variables that are storing that object. So if
wallet.money.amount = 2000, thenmyStuff.get('wallet').money.amountwill also be changed (unwillingly). It can sometimes cause weird behaviour in the application (trust me, I have been there before)Nested attributes can not be listened to. In Backbone, a change to an attribute can be listened to by using
change:[attribute]event. How about nested attributes? Nobody cares about nested attributes, that's why you can't listen to their changes, and they feel lonely, tooGet and set is not-so-backbone-style. So to change nested attribute value, one must do this
myStuff.get('wallet').money.amount = 6000, it's totally ok to do this, it's just ugly and inconvenient. How about validation? there is no way we can validate it. How about previous value? forget it. We loose all the benefits ofBackbone.Modelwhen we use nested attribute that way
SuperModel to the rescue!!!
With SuperModel we will have a (maybe) better way to deal with nested attributes. Here is the list of some features
Support nested set and get
Each level of nested attribute is represented by a model or a collection depend on the value. It also supports relations which will be discussed later.
myStuff.get('wallet.money.amount');
myStuff.set('wallet.money.amount', 6000);
wallet.money.amout = 0; // this won't affect the data inside SuperModelArrayof number, string is reserved as it isArrayof object is turned intoCollection- Empty
Arrayis turned intoCollection Objectis turned intoModel- If the path points to a collection, the value is set as a key of the collection itself
Support nested change events
Although it is not recommended to have deep-nested models, SuperModel supports it anyway. There are several change events when using set. Note that, the deeper the path goes, the more events will be fired. Therefore, be careful when using deep nested paths.
// receive 6000 as the changed value
view.listenTo(myStuff, 'change:wallet.money.amount', view.doThing);
// receive myStull.get('wallet.money') as the changed value
view.listenTo(myStuff, 'change:wallet.money', view.doThing);
// receive myStull.get('wallet') as the changed value
view.listenTo(myStuff, 'change:wallet', view.doThing);
// receive myStull.get('wallet.money') as the changed value
view.listenTo(myStull.get('wallet'), 'change:money', view.doThing);
// receive 6000 as the changed value
view.listenTo(myStull.get('wallet'), 'change:money.amount', view.doThing);
// receive 6000 as the changed value
view.listenTo(myStuff.get('wallet.money'), 'change:amount', view.doThing);
myStuff.set('wallet.money.amount', 6000);toJSON
The standard toJSON is now able to return the correct nested format
myStuff.set('wallet.money.amount', 6000); // will fire 2 different events
var json = myStuff.toJSON();
// json = {
// "wallet": {
// "money": {
// "amount": 6000
// }
// }
// }By settings unsafeAttributes (an array or a function which returns an array), toJSON will exclude those attributes in the returned value
var MyModel = Backbone.SuperModel.extend({
'unsafeAttributes': ['password']
});
var myModel = new MyModel({
'username': 'supermodel',
'password': 'secret'
});
myModel.toJSON(); // return {"username": "supermodel"}Support relations
By defining relations, SuperModel can automatically initiate the corresponding class when it processes the attributes
var AnotherModel = SuperModel.extend({
hello: function() {
return this.get('hello');
}
});
var Child = SuperModel.extend({
name: function() {
return this.get('name');
}
});
var Children = Backbone.Collection.extend({
model: Child,
firstChild: function() {
return this.at(0);
}
});
var MyModel = SuperModel.extend({
relations: {
'another': AnotherModel,
'children': Children
}
});
var model = new MyModel({
'another': {
'hello': 'SuperModel'
},
'children': [
{
'name': 'First baby'
},
{
'name': 'Second baby'
}
]
});
model.get('another').hello(); // returns 'SuperModel'
model.get('children'); // returns Children object
model.get('children').firstChild(); // returns Child object
model.get('children').firstChild().name(); // returns 'First baby'At the moment, the relation must be an instance of Backbone.Model or Backbone.Collection
Support backward reference
In some cases it might be useful that we can refer back to the upper level. For example, when rendering a list of posts, we might need to get the author information associated with each post. By setting name (can be a value or function), SuperModel will automatically build the back reference, if there is already an attribute with the same name, SuperModel will respect and skip it. The back reference is simply an attribute in the target model/collection in order to avoid circular reference when calling other methods such as toJSON
var Post = Backbone.SuperModel.extend({
});
var Posts = Backbone.Collection.extend({
model: Post
});
var User = Backbone.SuperModel.extend({
name: 'user',
relations: {
'posts': Posts
}
});
var user = new User({
'username': 'supermodel'
'posts': [
{
'title': 'Post number 1'
},
{
'title': 'Post number 2'
}
]
});
user.get('posts').at(0).user.get('username'); // return 'supermodel'TODOs
- Support validation
- Support as many
Backbone.Modelmethods as possible Support relationsSupport back reference- Implement
getRelationto replace the current way of accessing relations - Improve performance
Benchmarks
Running benchmark model_get [benchmarks/model_get.js]...
>> model_get x 48,381,420 ops/sec ±0.96% (93 runs sampled)
Running benchmark model_set [benchmarks/model_set.js]...
>> model_set x 300,337 ops/sec ±1.49% (96 runs sampled)
Running benchmark model_setget [benchmarks/model_setget.js]...
>> model_setget x 130,590 ops/sec ±1.49% (95 runs sampled)
Running benchmark supermodel_get [benchmarks/supermodel_get.js]...
>> supermodel_get x 4,487,165 ops/sec ±0.61% (98 runs sampled)
Running benchmark supermodel_nested_get [benchmarks/supermodel_nested_get.js]...
>> supermodel_nested_get x 1,999,848 ops/sec ±1.30% (95 runs sampled)
Running benchmark supermodel_nested_set [benchmarks/supermodel_nested_set.js]...
>> supermodel_nested_set x 45,619 ops/sec ±1.62% (95 runs sampled)
Running benchmark supermodel_set [benchmarks/supermodel_set.js]...
>> supermodel_set x 68,673 ops/sec ±1.12% (99 runs sampled)
Running benchmark supermodel_setget [benchmarks/supermodel_setget.js]...
>> supermodel_setget x 78,513 ops/sec ±2.04% (94 runs sampled)It is obviously slower than the original Backbone.Model
Development
Gruntfile.js, package.json and bower.json are your friends
Contributors
https://github.com/laoshanlung/backbone.supermodel/graphs/contributors
License
MIT
11 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago