Multiple Class Inheritance In Backbone Collections
<!DOCTYPE html>
<html>
<head>
<title>Multiple Class Inheritance In Backbone Collections</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>
</head>
<body>
<h1>
Multiple Class Inheritance In Backbone Collections
</h1>
<script type="text/javascript">
/**
*
*/
var idbDecorator = function(parent){
var sync = parent.prototype.sync;
var IDBCollection = parent.extend({
sync: function(){
console.log('IDB sync');
sync.call(this);
},
idb: function(){
console.log(this);
}
});
return IDBCollection;
};
/**
*
*/
var dualDecorator = function(parent){
if(!_.includes(parent._extended, 'idb')){
parent = parent.decorate('idb');
}
var sync = parent.prototype.sync;
var DualCollection = parent.extend({
sync: function(){
console.log('Dual sync');
sync.call(this);
}
});
return DualCollection;
};
/**
*
*/
var filteredDecorator = function(parent){
return parent;
};
/**
*
*/
var decoratorHash = {
dual : dualDecorator,
idb : idbDecorator,
filtered : filteredDecorator,
};
/**
*
*/
var Collection = Backbone.Collection.extend({
constructor: function () {
Backbone.Collection.apply(this, arguments);
this.isNew(true);
this._parent = Object.getPrototypeOf( Object.getPrototypeOf(this) );
},
isNew: function(reset) {
if(reset){
this._isNew = true;
this.once('sync', function() {
this._isNew = false;
});
}
return this._isNew;
},
sync: function(){
console.log('Original sync');
}
});
/**
*
*/
Collection.extend = function(protoProps, staticProps){
var parent = this;
var child;
var extend;
if (protoProps && _.has(protoProps, 'extends')) {
var extend = _.isString(protoProps.extends) ? [protoProps.extends] : protoProps.extends;
}
// russian doll decorators
if(extend && _.isArray(extend)){
_.each(extend, function(key){
if(!_.includes(parent._extended, key)){
parent = parent.decorate(key);
}
});
}
// The constructor function for the new subclass is either defined by you
// (the "constructor" property in your `extend` definition), or defaulted
// by us to simply call the parent constructor.
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = function(){ return parent.apply(this, arguments); };
}
// Add static properties to the constructor function, if supplied.
_.extend(child, parent, staticProps);
// Set the prototype chain to inherit from `parent`, without calling
// `parent`'s constructor function and add the prototype properties.
child.prototype = _.create(parent.prototype, protoProps);
child.prototype.constructor = child;
// Set a convenience property in case the parent's prototype is needed
// later.
child.__super__ = parent.prototype;
return child;
};
/**
*
*/
Collection.decorate = function(key, func){
var parent;
if(_.isFunction(func)){
parent = func(this);
} else {
parent = _.has(decoratorHash, key) ? decoratorHash[key](this) : this;
}
_.isArray(parent._extended) ? parent._extended.push(key) : parent._extended = [key];
return parent;
};
/**
*
*/
var ExtendedCollection = Collection.extend({
extends: ['dual', 'idb'],
sync: function(){
console.log('Custom sync');
this._parent.sync.call(this);
}
});
/**
*
*/
var NormalCollection = Collection.extend({
sync: function(){
console.log('Custom sync');
this._parent.sync.call(this);
}
});
/**
*
*/
console.log('-- without extends --');
var normal = new NormalCollection();
normal.sync();
console.log('-- with extends --');
var extended = new ExtendedCollection();
extended.sync();
</script>
</body>
</html>