Javascript Models Defaults: Any well defined techniques?

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?

Answers


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.


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.


Need Your Help

How to set openmp thread stack to unlimited?

fortran openmp intel-fortran stack-size

Can someone tell me how to set OpenMP stack size to unlimited?

What's the feature in Mercurial that gives me "checkpoints" that I can diff against without commiting frequent, breaking, small changes?

mercurial workflow diff

I'm developing a project, and I am making small, exploratory changes. After each change, I'd like to diff against the previous version I had. However, I don't want to commit each of these small cha...