- promise based
- typed attributes
- nested models and collections
- async calculations and validation
$npm install --save promised-models
var Model = require('promises-models'),
FashionModel = new Model.inherit({
attributes: {
name: Model.attributeTypes.String
}
}),
model = new FashionModel({
name: 'Kate'
});
model.get('name'); // 'Kate'
Creates you own model class by extending Model
. You can define attributes, instance/class method and properties. Inheritance is built over inherit.
var CountedModels = Model.inherit({
__constructor: function () {
this.__base.apply(this, arguments); //super
this.attributes.index.set(this.__self._count); //static properties
this.__self._count ++;
},
getIndex: function () {
return this.get('index');
}
}, {
_count: 0,
getCount: function () {
return this._count;
}
});
Namespace for predefined types of attributes. Supported types:
String
Number
Boolean
List
— for storing arraysModel
— for nested modelsModelsList
— for nested collectionsObject
— serializable objects
You can extend default attribute types or create your own
var DateAttribute = Model.attributeTypes.Number.inherit({
//..
}),
FashionModel = Model.inherit({
attributes: {
name: Model.attributeTypes.String,
birthDate: DateAttribute
}
});
Note: models.attributes
will be replaced in constructor with attribute instances.
var model = new FashionModel();
model.attributes.birthDate instanceof DateAttribute; //true
Set current value of attribute.
var model = new FashionModel();
model.set('name', 'Kate');
model.attributes.name.set('Kate');
model.set({
name: 'Kate',
birthDate: new Date(1974, 1, 16)
});
Note: setting null
is equivalent to call .unset()
Get current value of attribute.
var model = new FashionModel({
name: 'Kate',
birthDate: new Date(1974, 1, 16)
})
model.get('name'); //Kate
model.attributes.name.get(); //Kate
model.get('some'); //throws error as unknown attribute
Return shallow copy of model data.
Note: You can create internal attributes, which wouldn't be included to returned object.
var FashionModel = new Model.inherit({
attributes: {
name: Model.attributeTypes.String.inherit({
internal: true;
}),
sename: Model.attributeTypes.String.inherit({
internal: true;
}),
fullName: Model.attributeTypes.String
}
}),
model = new FashionModel({
name: 'Kate',
sename: 'Moss',
fullName: 'Kate Moss'
});
model.toJSON(); // {fullName: 'Kate Moss'}
model.get('name'); // Kate
Note: Returned object supposed to be serializable via JSON.parse()
. Due to this reason NaN
and Infinity
are serialized in this way:
NaN -> null
Infinity -> 'Infinity'
Has model changed since init or last commit/save/fetch.
var FashionModel = Model.inherit({
attributes: {
name: Model.attributeTypes.String,
weight: Model.attributeTypes.Number.inherit({
default: 50
})
}
}),
model = new FashionModel({
name: 'Kate',
weight: 55
});
model.isChanged(); //false
model.set('weight', 56);
model.isChanged(); //true
Cache current model state
var model = new FashionModel();
model.set({
name: 'Kate',
weight: 55
});
model.isChanged();//true
model.commit();
model.isChanged();//false
Revert model state to last cashed one
var model = new FashionModel({
name: 'Kate',
weight: 55
});
model.set('weight', 56);
model.revert();
model.get('weight'); //55
model.isChanged(); //false
Note: You can create your own cache by passing branch param.
var RENDERED = 'RENDERED';
model.on('change', function () {
if (model.isChanged(RENDERED)) {
View.render();
model.commit(RENDERED);
}
});
Add event handler for one or multiple model events.
List of events:
change
– some of attributes have been changedchange:attributeName
–attributeName
have been changeddestruct
– model was destructedcalculate
– async calculations started
model.on('change', this.changeHandler, this)
.on('change:weight change:name', this.changeHandler, this);
Unsubscribe event handler from events.
//subscribe
model.on('weight name', 'change', this.changeHandler, this);
//unsubscribe
model.un('change:weight change:name', this.changeHandler, this);
Remove all events handlers from model and removes model from collections
Returns true
if attribute was set via constructor or set
var model = new FashionModel();
model.isSet('name'); //false
model.set('name', 'Kate');
model.isSet('name'); //true
Set attribute to default value and model.isSet() === 'false'
var model = new FashionModel();
model.set('name', 'Kate');
model.unset('name');
model.isSet('name'); //false
model.get('name'); //empty string (default value)
Validate model attributes.
var FashionModel = Model.inherit({
attributes: {
name: Model.attributeTypes.String.inherit({
validate: function () {
return $.get('/validateName', {
name: this.get()
}).then(function () {
return true; //valid
}, function () {
return false; //invalid
});
}
})
}
}),
model = new FashionModel();
model.validate().fail(function (err) {
if (err instanceof Model.ValidationError) {
console.log('Invalid attributes:' + err.attributes.join());
} else {
return err;
}
}).done();
Fulfils when all calculations over model finished.
var FashionModel = Model.inherit({
attributes: {
name: Model.attributeTypes.String,
ratingIndex: Model.attributeTypes.Number.inherit({
calculate: function () {
return $.get('/rating', {
annualFee: this.model.get('annualFee')
});
}
}),
annualFee: Model.attributeTypes.Number
}
}),
model = new FashionModel();
model.set('annualFee', 1000000);
model.ready().then(function () {
model.get('ratingIndex');
}).done();
Fetch data associated with model from storage.
var FashionModel = Model.inherit({
attributes: {
name: Model.attributeTypes.String
},
storage: Model.Storage.inherit({
find: function (model) {
return $.get('/models', {
id: model.id
});
}
})
}),
model = new FashionModel(id);
model.fetch().then(function () {
model.get('name');
}).done();
var FashionModel = Model.inherit({
attributes: {
name: Model.attributeTypes.String,
weight: Model.attributeTypes.Number
},
storage: Model.Storage.inherit({
insert: function (model) {
return $.post('/models', model.toJSON()).then(function (result) {
return result.id;
});
},
update: function (model) {
return $.put('/models', model.toJSON());
}
})
}),
model = new FashionModel();
model.set({
name: 'Kate',
weight: 55
});
model.save().then(function () { //create
model.id; //storage id
model.set('weight', 56);
return model.save(); //update
}).done()
Removes model from storage.
model.isNew()
model.isReady()
model.trigger(event)
model.calculate()
model.CHANGE_BRANCH
model.CALCULATIONS_BRANCH
These methods provided for advanced model extending. Consult source for details.
Abstract class for model storage
var FashionModel = Model.inherit({
attributes: {
//..
},
storage: Model.Storage.inherit({
//..
})
});
Storage class
var SuperModel = FashionModel.inherit({
storage: FashionModel.storage.inherit({ //extend storage from FashionModel
//..
})
});
Base class for model attribute
var CustomAttribute = Model.attribute.inherit({
//..
})
Model class attributes
var SuperModel = FashionModel.inherit({
attributes: {
name: FashionModel.attributes.name,
weight: FashionModel.attributes.weight.inherit({
default: 50
})
}
});
Bind event on all models of class
FashionModel.on('change', this.changeHandler, this);
Unbind event on all models of class
Array like object returned for fields types List
and ModelsList
var Podium = Model.inherit({
attributes: {
models: Model.attributeTypes.ModelsList(FashionModel)
}
}),
podium = new Podium(data),
list = podium.get('models'), //instanceof List
model = list.get(0); //instanceof Model
List inerits Array mutating methods: pop
, push
, reverse
, shift
, sort
, splice
, unshift
podium.get('models').push(new FashionModel());
Get list item by index
podium.get('models').get(0);// instanceof Model
Returns length of list
Returns shallow copy of Array, wich stores List items
podium.get('models').forEach(function (model) {
model; // instanceof Model
});
Error class for validation fail report
$ npm test