Remove knockout evaluatorFunctionTarget

Drop deprecated knockout throttle extender
Added knockout debounce extender
This commit is contained in:
djmaze 2021-02-10 11:33:36 +01:00
parent b9b0a550d6
commit 75b7176ada
15 changed files with 158 additions and 226 deletions

View file

@ -64,7 +64,7 @@
// When a "parent" context is given and we don't already have a dependency on its context, register a dependency on it.
// Thus whenever the parent context is updated, this context will also be updated.
if (parentContext && parentContext[contextSubscribable] && !ko.computedContext.computed().hasAncestorDependency(parentContext[contextSubscribable])) {
if (parentContext && parentContext[contextSubscribable] && !ko.dependencyDetection.computed().hasAncestorDependency(parentContext[contextSubscribable])) {
parentContext[contextSubscribable]();
}
@ -374,7 +374,7 @@
}
return bindings;
},
null, { disposeWhenNodeIsRemoved: node }
{ disposeWhenNodeIsRemoved: node }
);
if (!bindings || !bindingsUpdater.isActive())
@ -453,7 +453,6 @@
if (typeof handlerUpdateFn == "function") {
ko.computed(
() => handlerUpdateFn(node, getValueAccessor(bindingKey), allBindings, contextToExtend['$data'], contextToExtend),
null,
{ disposeWhenNodeIsRemoved: node }
);
}

View file

@ -11,12 +11,9 @@ ko.bindingHandlers['html'] = {
let html = ko.utils.unwrapObservable(valueAccessor());
if (html != null) {
if (typeof html != 'string')
html = html.toString();
const template = document.createElement('template');
template.innerHTML = html;
element.appendChild(template.content);
const template = document.createElement('template');
template.innerHTML = typeof html != 'string' ? html.toString() : html;
element.appendChild(template.content);
}
}
}
};

View file

@ -31,12 +31,12 @@ function makeWithIfBinding(bindingKey, isWith, isNot) {
if (shouldDisplay) {
if (!isWith || renderOnEveryChange) {
contextOptions['dataDependency'] = ko.computedContext.computed();
contextOptions['dataDependency'] = ko.dependencyDetection.computed();
}
if (isWith) {
childContext = bindingContext['createChildContext'](typeof value == "function" ? value : valueAccessor, contextOptions);
} else if (ko.computedContext.getDependenciesCount()) {
} else if (ko.dependencyDetection.getDependenciesCount()) {
childContext = bindingContext['extend'](null, contextOptions);
} else {
childContext = bindingContext;
@ -44,7 +44,7 @@ function makeWithIfBinding(bindingKey, isWith, isNot) {
}
// Save a copy of the inner nodes on the initial update, but only if we have dependencies.
if (isInitial && ko.computedContext.getDependenciesCount()) {
if (isInitial && ko.dependencyDetection.getDependenciesCount()) {
savedNodes = ko.utils.cloneNodes(ko.virtualElements.childNodes(element), true /* shouldCleanNodes */);
}
@ -64,7 +64,7 @@ function makeWithIfBinding(bindingKey, isWith, isNot) {
didDisplayOnLastUpdate = shouldDisplay;
}, null, { disposeWhenNodeIsRemoved: element });
}, { disposeWhenNodeIsRemoved: element });
return { 'controlsDescendantBindings': true };
}

View file

@ -144,7 +144,7 @@ ko.bindingHandlers['options'] = {
}
}
if (valueAllowUnset || ko.computedContext.isInitial()) {
if (valueAllowUnset || ko.dependencyDetection.isInitial()) {
ko.bindingEvent.notify(element, ko.bindingEvent.childrenComplete);
}

View file

@ -72,7 +72,7 @@ ko.bindingHandlers['textInput'] = {
// To deal with browsers that don't notify any kind of event for some changes (IE, Safari, etc.)
onEvent('blur', updateModel);
ko.computed(updateView, null, { disposeWhenNodeIsRemoved: element });
ko.computed(updateView, { disposeWhenNodeIsRemoved: element });
}
};
ko.expressionRewriting.twoWayBindings['textInput'] = true;

View file

@ -99,7 +99,7 @@ ko.bindingHandlers['value'] = {
ko.bindingEvent.subscribe(element, ko.bindingEvent.childrenComplete, () => {
if (!updateFromModelComputed) {
ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
updateFromModelComputed = ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
updateFromModelComputed = ko.computed(updateFromModel, { disposeWhenNodeIsRemoved: element });
} else if (allBindings.get('valueAllowUnset')) {
updateFromModel();
} else {
@ -108,7 +108,7 @@ ko.bindingHandlers['value'] = {
}, null, { 'notifyImmediately': true });
} else {
ko.utils.registerEventHandler(element, "change", valueUpdateHandler);
ko.computed(updateFromModel, null, { disposeWhenNodeIsRemoved: element });
ko.computed(updateFromModel, { disposeWhenNodeIsRemoved: element });
}
},
'update': () => {} // Keep for backwards compatibility with code that may have wrapped value binding

View file

@ -36,7 +36,7 @@
// of which nodes would be deleted if valueToMap was itself later removed
mappedNodes.length = 0;
mappedNodes.push(...newMappedNodes);
}, null, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: ()=>!!mappedNodes.find(ko.utils.domNodeIsAttachedToDocument) });
}, { disposeWhenNodeIsRemoved: containerNode, disposeWhen: ()=>!!mappedNodes.find(ko.utils.domNodeIsAttachedToDocument) });
return { mappedNodes : mappedNodes, dependentObservable : (dependentObservable.isActive() ? dependentObservable : undefined) };
}

View file

@ -77,7 +77,7 @@
currentViewModel = componentViewModel;
ko.applyBindingsToDescendants(childBindingContext, element);
});
}, null, { disposeWhenNodeIsRemoved: element });
}, { disposeWhenNodeIsRemoved: element });
return { 'controlsDescendantBindings': true };
}

View file

@ -1,5 +1,5 @@
ko.computedContext = ko.dependencyDetection = (() => {
ko.dependencyDetection = (() => {
var outerFrames = [],
currentFrame,
lastId = 0,
@ -38,10 +38,6 @@ ko.computedContext = ko.dependencyDetection = (() => {
return currentFrame && currentFrame.computed.getDependenciesCount();
},
getDependencies: () => {
return currentFrame && currentFrame.computed.getDependencies();
},
isInitial: () => {
return currentFrame && currentFrame.isInitial;
},

View file

@ -1,6 +1,6 @@
var computedState = Symbol('_state');
ko.computed = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
ko.computed = function (evaluatorFunctionOrOptions, options) {
if (typeof evaluatorFunctionOrOptions === "object") {
// Single-parameter syntax - everything is on this "options" param
options = evaluatorFunctionOrOptions;
@ -25,7 +25,6 @@ ko.computed = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, opt
pure: false,
isSleeping: false,
readFunction: options["read"],
evaluatorFunctionTarget: evaluatorFunctionTarget || options["owner"],
disposeWhenNodeIsRemoved: options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
disposeWhen: options["disposeWhen"] || options.disposeWhen,
domNodeDisposalCallback: null,
@ -38,7 +37,7 @@ ko.computed = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, opt
if (arguments.length > 0) {
if (typeof writeFunction === "function") {
// Writing a value
writeFunction.apply(state.evaluatorFunctionTarget, arguments);
writeFunction(...arguments);
} else {
throw new Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
}
@ -327,8 +326,7 @@ var computedFn = {
// overhead of computed evaluation (on V8 at least).
try {
var readFunction = state.readFunction;
return state.evaluatorFunctionTarget ? readFunction.call(state.evaluatorFunctionTarget) : readFunction();
return state.readFunction();
} finally {
ko.dependencyDetection.end();
@ -395,9 +393,6 @@ var computedFn = {
state.disposeWhenNodeIsRemoved = undefined;
state.disposeWhen = undefined;
state.readFunction = undefined;
if (!this.hasWriteFunction) {
state.evaluatorFunctionTarget = undefined;
}
}
};
@ -491,11 +486,11 @@ ko.exportSymbol('computed', ko.computed);
ko.exportSymbol('computed.fn', computedFn);
ko.exportProperty(computedFn, 'dispose', computedFn.dispose);
ko.pureComputed = (evaluatorFunctionOrOptions, evaluatorFunctionTarget) => {
ko.pureComputed = (evaluatorFunctionOrOptions) => {
if (typeof evaluatorFunctionOrOptions === 'function') {
return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget, {'pure':true});
return ko.computed(evaluatorFunctionOrOptions, {'pure':true});
}
evaluatorFunctionOrOptions = ko.utils.extend({}, evaluatorFunctionOrOptions); // make a copy of the parameter object
evaluatorFunctionOrOptions['pure'] = true;
return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget);
return ko.computed(evaluatorFunctionOrOptions);
};

View file

@ -1,22 +1,5 @@
ko.extenders = {
'throttle': (target, timeout) => {
// Throttling means two things:
// (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
// notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
target['throttleEvaluation'] = timeout;
// (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
// so the target cannot change value synchronously or faster than a certain rate
var writeTimeoutInstance = null;
return ko.computed({
'read': target,
'write': value => {
clearTimeout(writeTimeoutInstance);
writeTimeoutInstance = ko.utils.setTimeout(() => target(value), timeout);
}
});
},
'debounce': (target, timeout) => target.limit(callback => debounce(callback, timeout)),
'rateLimit': (target, options) => {
var timeout, method, limitFunction;
@ -28,7 +11,7 @@ ko.extenders = {
method = options['method'];
}
limitFunction = typeof method == 'function' ? method : method == 'notifyWhenChangesStop' ? debounce : throttle;
limitFunction = typeof method == 'function' ? method : throttle;
target.limit(callback => limitFunction(callback, timeout, options));
},
@ -41,8 +24,7 @@ ko.extenders = {
var primitiveTypes = { 'undefined':1, 'boolean':1, 'number':1, 'string':1 };
function valuesArePrimitiveAndEqual(a, b) {
var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
return oldValueIsPrimitive ? (a === b) : false;
return (a === null || primitiveTypes[typeof(a)]) ? (a === b) : false;
}
function throttle(callback, timeout) {
@ -50,7 +32,7 @@ function throttle(callback, timeout) {
return () => {
if (!timeoutInstance) {
timeoutInstance = ko.utils.setTimeout(() => {
timeoutInstance = undefined;
timeoutInstance = 0;
callback();
}, timeout);
}
@ -65,17 +47,4 @@ function debounce(callback, timeout) {
};
}
function applyExtenders(requestedExtenders) {
var target = this;
if (requestedExtenders) {
ko.utils.objectForEach(requestedExtenders, (key, value) => {
var extenderHandler = ko.extenders[key];
if (typeof extenderHandler == 'function') {
target = extenderHandler(target, value) || target;
}
});
}
return target;
}
ko.exportSymbol('extenders', ko.extenders);

View file

@ -171,7 +171,18 @@ var ko_subscribable_fn = {
toString: () => '[object Object]',
extend: applyExtenders
extend: function(requestedExtenders) {
var target = this;
if (requestedExtenders) {
ko.utils.objectForEach(requestedExtenders, (key, value) => {
var extenderHandler = ko.extenders[key];
if (typeof extenderHandler == 'function') {
target = extenderHandler(target, value) || target;
}
});
}
return target;
}
};
ko.exportProperty(ko_subscribable_fn, 'init', ko_subscribable_fn.init);

View file

@ -124,7 +124,6 @@
var templateName = resolveTemplateName(template, bindingContext['$data'], bindingContext);
executeTemplate(targetNodeOrNodeArray, renderMode, templateName, bindingContext, options);
},
null,
{ disposeWhen: whenToDispose, disposeWhenNodeIsRemoved: firstTargetNode }
);
} else {
@ -196,7 +195,7 @@
}
setDomNodeChildrenFromArrayMapping(unwrappedArray);
}, null, { disposeWhenNodeIsRemoved: targetNode });
}, { disposeWhenNodeIsRemoved: targetNode });
}
};