Permalink
Browse files

Binding attribute's internal dependentObservable now proactively disp…

…oses itself as soon as the associated node is removed using ko.removeNode
  • Loading branch information...
1 parent 51832c2 commit ae3410f7e4cb7cdbf35d700878a51c6edbd2009a @SteveSanderson SteveSanderson committed Mar 20, 2011
@@ -114,19 +114,29 @@ describe('Binding attribute syntax', {
value_of(updatePassedValues[1]).should_be("A");
},
- 'If the associated DOM element was removed, handler subscriptions are disposed': function () {
+ 'If the associated DOM element was removed by KO, handler subscriptions are disposed immediately': function () {
var observable = new ko.observable("A");
- var passedValues = [];
- ko.bindingHandlers.test = { update: function (element, valueAccessor) { passedValues.push(valueAccessor()); } };
- testNode.innerHTML = "<div data-bind='test: myObservable()'></div>";
-
+ testNode.innerHTML = "<div data-bind='anyHandler: myObservable()'></div>";
ko.applyBindings({ myObservable: observable }, testNode);
- observable("B");
- value_of(passedValues.length).should_be(2);
+
+ value_of(observable.getSubscriptionsCount()).should_be(1);
+
+ ko.removeNode(testNode);
+
+ value_of(observable.getSubscriptionsCount()).should_be(0);
+ },
+ 'If the associated DOM element was removed independently of KO, handler subscriptions are disposed on the next evaluation': function () {
+ var observable = new ko.observable("A");
+ testNode.innerHTML = "<div data-bind='anyHandler: myObservable()'></div>";
+ ko.applyBindings({ myObservable: observable }, testNode);
+
+ value_of(observable.getSubscriptionsCount()).should_be(1);
+
testNode.parentNode.removeChild(testNode);
- observable("C");
- value_of(passedValues.length).should_be(2);
+ observable("B"); // Force re-evaluation
+
+ value_of(observable.getSubscriptionsCount()).should_be(0);
},
'If the binding attribute involves an observable, re-invokes the bindings if the observable notifies a change': function () {
@@ -53,7 +53,7 @@
}
},
null,
- { 'disposeWhen' : function () { return !ko.utils.domNodeIsAttachedToDocument(node); } }
+ { 'disposeWhenNodeIsRemoved' : node }
);
isFirstEvaluation = false;
};
@@ -1,5 +1,7 @@
ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
+ var _latestValue, _hasBeenEvaluated = false;
+
if (evaluatorFunctionOrOptions && typeof evaluatorFunctionOrOptions == "object") {
// Single-parameter syntax - everything is on this "options" param
options = evaluatorFunctionOrOptions;
@@ -14,6 +16,20 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
if (typeof options["read"] != "function")
throw "Pass a function that returns the value of the dependentObservable";
+ // "disposeWhenNodeIsRemoved" option both proactively disposes as soon as the node is removed using ko.removeNode(),
+ // plus adds a "disposeWhen" callback that, on each evaluation, disposes if the node was removed by some other means.
+ if (typeof options["disposeWhenNodeIsRemoved"] == "object") {
+ var nodeToWatch = options["disposeWhenNodeIsRemoved"];
+ ko.utils.domNodeDisposal.addDisposeCallback(nodeToWatch, function() {
+ dependentObservable.dispose();
+ });
+ var existingDisposeWhenFunction = options["disposeWhen"];
+ options["disposeWhen"] = function () {
+ return (!ko.utils.domNodeIsAttachedToDocument(nodeToWatch))
+ || ((typeof existingDisposeWhenFunction == "function") && existingDisposeWhenFunction());
+ }
+ }
+
var _subscriptionsToDependencies = [];
function disposeAllSubscriptionsToDependencies() {
ko.utils.arrayForEach(_subscriptionsToDependencies, function (subscription) {
@@ -28,8 +44,7 @@ ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunction
_subscriptionsToDependencies.push(dependency.subscribe(evaluate));
});
};
-
- var _latestValue, _hasBeenEvaluated = false;
+
function evaluate() {
// Don't dispose on first evaluation, because the "disposeWhen" callback might
// e.g., dispose when the associated DOM element isn't in the doc, and it's not

0 comments on commit ae3410f

Please sign in to comment.