From b7decfd1551e735213e2e64445ca4ece3d059a40 Mon Sep 17 00:00:00 2001 From: Michael Best Date: Tue, 17 Oct 2017 11:22:09 -0700 Subject: [PATCH] Make sure a null afterRender doesn't fail + add some tests --- spec/bindingAttributeBehaviors.js | 5 +++++ spec/bindingDependencyBehaviors.js | 32 +++++++++++++++++++++++++++ src/binding/bindingAttributeSyntax.js | 15 ++++++++----- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/spec/bindingAttributeBehaviors.js b/spec/bindingAttributeBehaviors.js index 1291c53ab..9504ccef3 100644 --- a/spec/bindingAttributeBehaviors.js +++ b/spec/bindingAttributeBehaviors.js @@ -621,4 +621,9 @@ describe('Binding attribute syntax', function() { ko.applyBindings({ callback: function () { callbacks++; } }, testNode); expect(callbacks).toEqual(0); }); + + it('Should ignore (and not throw an error) for a null afterRender callback', function () { + testNode.innerHTML = "
"; + ko.applyBindings({}, testNode); + }); }); diff --git a/spec/bindingDependencyBehaviors.js b/spec/bindingDependencyBehaviors.js index 1fa714ab0..4d5513fef 100644 --- a/spec/bindingDependencyBehaviors.js +++ b/spec/bindingDependencyBehaviors.js @@ -289,6 +289,38 @@ describe('Binding dependencies', function() { expect(callbacks).toEqual(2); }); + it('Should always use the latest value of an afterRender callback', function () { + ko.bindingHandlers.test = { + init: function() { + return { controlsDescendantBindings: true }; + }, + update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { + var innerContext = bindingContext.createChildContext({childprop: ko.utils.unwrapObservable(valueAccessor())}); + element.innerHTML = ""; + ko.applyBindingsToDescendants(innerContext, element); + } + }; + + var callbackSpy1 = jasmine.createSpy('callbackSpy1'), + callbackSpy2 = jasmine.createSpy('callbackSpy2'), + vm = { + observable: ko.observable('value'), + callback: callbackSpy1 + }; + + testNode.innerHTML = "
"; + ko.applyBindings(vm, testNode); + expect(callbackSpy1).toHaveBeenCalled(); + + callbackSpy1.reset(); + vm.callback = callbackSpy2; + + vm.observable('new value'); + expect(testNode.childNodes[0]).toContainText('new value'); + expect(callbackSpy1).not.toHaveBeenCalled(); + expect(callbackSpy2).toHaveBeenCalled(); + }); + describe('Observable view models', function() { it('Should update bindings (including callbacks)', function() { var vm = ko.observable(), clickedVM; diff --git a/src/binding/bindingAttributeSyntax.js b/src/binding/bindingAttributeSyntax.js index 72a3c27d6..acb95fc9d 100755 --- a/src/binding/bindingAttributeSyntax.js +++ b/src/binding/bindingAttributeSyntax.js @@ -235,12 +235,15 @@ var afterRender = ko.utils.domData.get(elementOrVirtualElement, afterRenderCallbackDomDataKey); if (afterRender) { - var nodes = ko.virtualElements.childNodes(elementOrVirtualElement); - if (nodes.length) { - ko.dependencyDetection.ignore(function () { - evaluateValueAccessor(afterRender)(nodes, ko.dataFor(nodes[0])); - }); - } + ko.dependencyDetection.ignore(function () { + afterRender = evaluateValueAccessor(afterRender); + if (afterRender) { + var nodes = ko.virtualElements.childNodes(elementOrVirtualElement); + if (nodes.length) { + afterRender(nodes, ko.dataFor(nodes[0])); + } + } + }); } } }