Skip to content

Instantly share code, notes, and snippets.

@DrSammyD
Last active August 29, 2015 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DrSammyD/9793884 to your computer and use it in GitHub Desktop.
Save DrSammyD/9793884 to your computer and use it in GitHub Desktop.
Knockout dependency detection in chained lodash or underscore calls.
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['knockout','lodash'], factory);
} else {
factory(ko,_);
}
})(function (ko, _) {
function isLodash(lodashCalls) {
return lodashCalls && typeof lodashCalls == 'object' && !_.isArray(lodashCalls) && hasOwnProperty.call(lodashCalls, '__wrapped__')
};
function kodashWrapper(obs) {
this.__observable__ = obs;
this.__funcs__ = [];
};
kodashWrapper['fn'] = kodashWrapper['prototype'];
var kodashPushFunction = function (wrapper, loFunc, args) {
wrapper.__funcs__.push({
loFunc: loFunc,
args: args,
comp: null,
rewrap: false
});
};
var unwrapArgs = function (args) {
var observableflag = false;
argArr=[];
_(args).reverse().each(function (item, index) {
var arg=item;
if (ko.isObservable(arg)) {
arg = ko.utils.unwrapObservable(arg)
}
argArr.push(arg)
});
return argArr;
};
var hasObservableArgs = function (args) {
var argRev = [];
return _(args).any(function (item) {
return ko.isObservable(item)
});
};
_.mixin(kodashWrapper.prototype, _(_.prototype).keys().object().mapValues(function (val, key) {
return function () {
var wrapper = new kodashWrapper(this.__observable__)
wrapper.__funcs__ = wrapper.__funcs__.concat(this.__funcs__);
kodashPushFunction(wrapper, key, arguments);
return wrapper;
}
}).value());
var rewrap=function(){
lastFunc = _.last(this.__funcs__);
if(lastFunc){
lastFunc["rewrap"]=true;
}
return this;
};
var mutap = function () {
if (ko.isWriteableObservable(this.__observable__)) {
var loVal = this.wrappedValueOf();
this.__funcs__ = [];
this.__observable__((loVal).valueOf());
}
return this.__observable__;
};
var wrappedValueOf = function () {
var lofuncs=_(this.__funcs__);
var lastLofuncs=lofuncs.last(function(item){ return !item.comp });
var length = -lastLofuncs.value().length;
length = length? length: this.__funcs__.length;
lastComp = lofuncs.slice(0,length).last();
if(lastComp){
var lodashCalls = _(lastComp.comp.peek());
lastLofuncs =lastLofuncs.rest()
}
else{
var lodashCalls = _(ko.unwrap(this.__observable__));
lastLofuncs= lofuncs;
}
lastLofuncs.each(function (func) {
args = unwrapArgs(func['args'])
if (isLodash(lodashCalls)) {
lodashCalls = lodashCalls[func['loFunc']].apply(lodashCalls, args);
}
});
return lodashCalls
};
var valueOf = function () {
return this.wrappedValueOf().valueOf();
};
var observe = function () {
var result = [];
_(this.__funcs__).each(function (item, index) {
var lastgroup = result.length ? _.last(result).group : 0;
var group = hasObservableArgs(item.args) ? lastgroup + 1 : lastgroup;
result.push({
group: group,
func: item
});
});
var computed = baseObservable = this.__observable__;
_(result).groupBy('group').each(function (group) {
computed = createComputedGroup(group, computed, baseObservable);
});
return createUnwrappedComputed(computed);
}
_.mixin(kodashWrapper.prototype, {
_: rewrap,
rewrap: rewrap,
mutap: mutap,
wrappedValueOf: wrappedValueOf,
wrappedValue: wrappedValueOf,
valueOf: valueOf,
value: valueOf,
observe: observe,
observeValueOf: observe
});
function createUnwrappedComputed(observable){
if(isLodash(observable.peek()))
return ko.computed(function(){return observable().valueOf()})
else
return observable;
}
function createComputedGroup(group, observable, baseObservable) {
var funcs = _(group).pluck('func')
var last =funcs.last();
if(last.comp){
return last.comp
}
return last.comp = ko.computed(function () {
var lodashCalls = ko.unwrap(observable);
lodashCalls= (observable === baseObservable)? _(lodashCalls) : lodashCalls
_(funcs).each(function (func) {
if (isLodash(lodashCalls)) {
var args = unwrapArgs(func['args'])
lodashCalls = lodashCalls[func['loFunc']].apply(lodashCalls, args);
lodashCalls = func.rewrap? _(lodashCalls) : lodashCalls;
}
});
return lodashCalls;
})
};
ko.observable['fn']['_'] = ko.observable['fn']['kodash'] = function () {
return new kodashWrapper(this)
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment