Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

#144 - arrayToDomNodeChildren/fixUpVirtualElements updates and specs

Add spec and expand comments on earlier change.
Fix up node list for any array with 2 or more items (rather than 3 or more).
Add spec for above.
  • Loading branch information...
commit 3ec00596c6200d17003a14865dd3fd2da87bf7cd 1 parent a3afdeb
@mbest mbest authored
View
19 spec/defaultBindingsBehaviors.js
@@ -1450,6 +1450,25 @@ describe('Binding: Foreach', {
value_of(testNode).should_contain_html('<div data-bind="foreach: someitems">a<!-- ko if:true -->b<!-- /ko --></div>');
},
+ 'Should remove all nodes corresponding to a removed array item, even if they were added via containerless syntax and there are no other nodes': function() {
+ ko.bindingHandlers.test = {
+ init: function (element, valueAccessor) {
+ var value = valueAccessor();
+ ko.virtualElements.prepend(element, document.createTextNode(value));
+ }
+ };
+ ko.virtualElements.allowedBindings['test'] = true;
+
+ testNode.innerHTML = "x-<!--ko foreach: someitems--><!--ko test:$data--><!--/ko--><!--/ko-->";
+ var someitems = ko.observableArray(["aaa","bbb"]);
+ ko.applyBindings({ someitems: someitems }, testNode);
+ value_of(testNode).should_contain_text('x-aaabbb');
+
+ // Now remove items, and check the corresponding child nodes vanished
+ someitems.splice(1, 1);
+ value_of(testNode).should_contain_text('x-aaa');
+ },
+
'Should update all nodes corresponding to a changed array item, even if they were generated via containerless templates': function() {
testNode.innerHTML = "<div data-bind='foreach: someitems'><!-- ko if:true --><span data-bind='text: $data'></span><!-- /ko --></div>";
var someitems = [ ko.observable('A'), ko.observable('B') ];
View
20 spec/templatingBehaviors.js
@@ -373,7 +373,7 @@ describe('Templating', {
testNode.innerHTML = "<div data-bind=\"template: { name: 'mytemplate', foreach: items }\"></div>";
// Bind against initial array containing one entry. UI just shows "original"
- var myArray = ko.observableArray(["original"]);
+ var myArray = ko.observableArray(["original"]);
ko.applyBindings({ items: myArray });
value_of(testNode.childNodes[0]).should_contain_html("<div>original</div>");
@@ -383,6 +383,24 @@ describe('Templating', {
value_of(testNode.childNodes[0]).should_contain_html("<div>new</div>");
},
+ 'Data binding \'foreach\' should handle chained templates in which the very first node has a binding': function() {
+ // See https://github.com/SteveSanderson/knockout/pull/440 and https://github.com/SteveSanderson/knockout/pull/144
+ ko.setTemplateEngine(new dummyTemplateEngine({
+ outerTemplate: "<div data-bind='text: $data'></div>[renderTemplate:innerTemplate]x", // [renderTemplate:...] is special syntax supported by dummy template engine
+ innerTemplate: "inner <span data-bind='text: 123'></span>"
+ }));
+ testNode.innerHTML = "<div data-bind=\"template: { name: 'outerTemplate', foreach: items }\"></div>";
+
+ // Bind against initial array containing one entry.
+ var myArray = ko.observableArray(["original"]);
+ ko.applyBindings({ items: myArray });
+ value_of(testNode.childNodes[0]).should_contain_html("<div>original</div>inner <span>123</span>x");
+
+ // Now replace the entire array contents with one different entry.
+ myArray(["new"]);
+ value_of(testNode.childNodes[0]).should_contain_html("<div>new</div>inner <span>123</span>x");
+ },
+
'Data binding \'foreach\' option should apply bindings with an $index in the context': function () {
var myArray = new ko.observableArray([{ personName: "Bob" }, { personName: "Frank"}]);
ko.setTemplateEngine(new dummyTemplateEngine({ itemTemplate: "The item # is <span data-bind='text: $index'></span>" }));
View
6 src/binding/editDetection/arrayToDomNodeChildren.js
@@ -11,7 +11,9 @@
// You can use this, for example, to activate bindings on those nodes.
function fixUpVirtualElements(contiguousNodeArray) {
- // Remove any initial nodes that aren't in the document
+ // Re-written templates insert comments into the node list during memoization and then remove them during un-memoization.
+ // If the first node in the list is one of those comments, we need to remove it from the list and get the actual first node.
+ // See https://github.com/SteveSanderson/knockout/pull/440
while (contiguousNodeArray.length && !ko.utils.domNodeIsAttachedToDocument(contiguousNodeArray[0]))
contiguousNodeArray.splice(0, 1);
@@ -20,7 +22,7 @@
// their virtual children changed when binding was applied to them).
// This is needed so that we can reliably remove or update the nodes corresponding to a given array item
- if (contiguousNodeArray.length > 2) {
+ if (contiguousNodeArray.length > 1) {
// Build up the actual new contiguous node set
var current = contiguousNodeArray[0], last = contiguousNodeArray[contiguousNodeArray.length - 1], newContiguousSet = [current];
while (current !== last) {

1 comment on commit 3ec0059

@domenic

Agh, why is the Knockout release cycle so slow. I've been hitting my head against this problem for a few days before finally tracking it down to this line. I wish it were released already :(

Please sign in to comment.
Something went wrong with that request. Please try again.