Reputation: 3413
I am writing angularjs model services. However I would like some convenience methods for setting up defaults and nesting models.
This is the solution I have arrived at.
var Model = function () { this.assign = function (obj, members_to_set) { if (typeof obj !== 'undefined') { angular.forEach(members_to_set, function (member, index) { if (typeof obj[member] !== 'undefined') { this[member] = obj[member]; } }, this); } }; this.assignList = function (member, obj, setter, default_) { this[member] = []; if (typeof obj !== 'undefined' && typeof obj[member] !== 'undefined') { angular.forEach(obj[member], function (val, index) { this[member].push(setter(val)); }, this); } else { this[member].push(default_); } }; }
To use it
var Car = function(car_data){ var defaults = { make: '', year: '', manufacturer: '' } this.assign(defaults, ['make', 'year']); this.assign(car_data, ['make', 'year']); //Similarly this.assignList('owners', car_data, function(owner_data){ return new Owner(owner_data); }, new Owner()} } Car.prototype = new Model();
But I am sure that these are solved problems with well understood solutions. I don't want to reinvent this particular wheel.
The question therefore is : are there better ways to do this?
Upvotes: 0
Views: 70
Reputation: 43718
There might be some libraries that would help with this, however these would probably also come with tons of features you do not need.
Having to apply default values can be a concern of many type of objects, therefore I would extract the behaviour in it's own function that can then be used from the Model
rather than making it part of the Model
class logic.
Also, if you want to be memory efficient, default values should be shared across all instances, not copied over every instances. For this reason, we avoid using angular.extend
.
I left out any modularization to keep the example simple but you should modularize your code!
//only extend when properties have a different value
function diffExtend(obj, data, keys) {
(keys || Object.keys(data)).forEach(function (k) {
if (obj[k] !== data[k]) obj[k] = data[k];
});
return obj;
}
//Base class for every models
function Model(data, keys) {
diffExtend(this, data, keys);
}
//Concrete Car model
function Car(data) {
//only copy over make and year, but we could avoid passing keys to copy everything
Model.call(this, data, ['make', 'year']);
}
Car.prototype = diffExtend(Object.create(Model.prototype), {
constructor: Car,
//default values (only for primitives because these aren't mutable)
make: 'default make',
year: 2000
});
//instanciate a new car
var c = new Car({ make: 'test', year: 2000, invalidProperty: true });
c.make; //test
c.year; //2000
c.hasOwnProperty('make'); //true
c.hasOwnProperty('year'); //false because it's on the prototype
c.invalidProperty; //undefined
Now for your assignList
function, you could simply use Array.prototype.map
.
E.g.
var data = [{ make: 'test1', year: 2001 }, { make: 'test2', year: 2002 }],
list = data?
data.map(function (d) { return new Model(d)}) :
new Model({ make: 'default model' });
Perhaps I haven't provided a full-blown solution to what you wanted, but I believe that you now should have the knowledge required to implement an efficient solution that exactly suits your needs.
Upvotes: 1
Reputation: 19249
A common way is to use extend
to set the defaults. extend
is supported by angular, jquery, underscore and probably many other libraries.
The usage is simple
function Foo(defaults){
this.a = 10;
this.b = 11;
angular.extend(this, defaults);
}
Then, when you create a Foo:
var f = new Foo({a:100, c:200, d: {d1:100, d2:200}});
console.log(f);
// Foo {a: 100, b: 11, c: 200, d: Object}
See: http://plnkr.co/edit/qRJRj8YfetSIcbRpxBc5?p=preview
But this is a lot less sophisticated than what you seem to be looking for. I hope this helps.
Upvotes: 0