From ad5beb83f6836390a9cfc1f95f073af973bcf6a4 Mon Sep 17 00:00:00 2001 From: Mark Bradley Date: Sat, 22 Oct 2011 14:42:33 +1100 Subject: [PATCH] added $index value in the binding context of foreach templates. --- spec/templatingBehaviors.js | 11 ++++++++++- src/binding/editDetection/arrayToDomNodeChildren.js | 10 +++++----- src/templating/templating.js | 12 +++++++----- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/spec/templatingBehaviors.js b/spec/templatingBehaviors.js index 77281e8e6..588a579e6 100644 --- a/spec/templatingBehaviors.js +++ b/spec/templatingBehaviors.js @@ -350,6 +350,15 @@ describe('Templating', { ko.applyBindings({ myCollection: myArray }, testNode); value_of(testNode.childNodes[0]).should_contain_html("
the item is bob
the item is frank
"); }, + + '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 " })); + testNode.innerHTML = "
"; + + ko.applyBindings({ myCollection: myArray }, testNode); + value_of(testNode.childNodes[0]).should_contain_html("
the item # is 0
the item # is 1
"); + }, 'Data binding \'foreach\' option should update DOM nodes when a dependency of their mapping function changes': function() { var myObservable = new ko.observable("Steve"); @@ -650,4 +659,4 @@ describe('Templating', { ko.applyBindings({ someData: { childProp: 'abc' } }, testNode); value_of(testNode).should_contain_html("start
childprop: abc
end"); } -}) \ No newline at end of file +}) diff --git a/src/binding/editDetection/arrayToDomNodeChildren.js b/src/binding/editDetection/arrayToDomNodeChildren.js index 6b1294aa3..ef2e48a3d 100644 --- a/src/binding/editDetection/arrayToDomNodeChildren.js +++ b/src/binding/editDetection/arrayToDomNodeChildren.js @@ -10,11 +10,11 @@ // "callbackAfterAddingNodes" will be invoked after any "mapping"-generated nodes are inserted into the container node // You can use this, for example, to activate bindings on those nodes. - function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes) { + function mapNodeAndRefreshWhenChanged(containerNode, mapping, valueToMap, callbackAfterAddingNodes, index) { // Map this array value inside a dependentObservable so we re-map when any dependency changes var mappedNodes = []; var dependentObservable = ko.dependentObservable(function() { - var newMappedNodes = mapping(valueToMap) || []; + var newMappedNodes = mapping(valueToMap, index) || []; // On subsequent evaluations, just replace the previously-inserted DOM nodes if (mappedNodes.length > 0) { @@ -77,9 +77,9 @@ case "added": var valueToMap = editScript[i].value; - var mapData = mapNodeAndRefreshWhenChanged(domNode, mapping, valueToMap, callbackAfterAddingNodes); + var mapData = mapNodeAndRefreshWhenChanged(domNode, mapping, valueToMap, callbackAfterAddingNodes, i); var mappedNodes = mapData.mappedNodes; - + // On the first evaluation, insert the nodes at the current insertion point newMappingResult.push({ arrayEntry: editScript[i].value, domNodes: mappedNodes, dependentObservable: mapData.dependentObservable }); for (var nodeIndex = 0, nodeIndexMax = mappedNodes.length; nodeIndex < nodeIndexMax; nodeIndex++) { @@ -99,7 +99,7 @@ insertAfterNode = node; } if (callbackAfterAddingNodes) - callbackAfterAddingNodes(valueToMap, mappedNodes); + callbackAfterAddingNodes(valueToMap, mappedNodes, i); break; } } diff --git a/src/templating/templating.js b/src/templating/templating.js index 2d09395be..67ed903e1 100644 --- a/src/templating/templating.js +++ b/src/templating/templating.js @@ -117,8 +117,10 @@ }; ko.renderTemplateForEach = function (template, arrayOrObservableArray, options, targetNode, parentBindingContext) { - var createInnerBindingContext = function(arrayValue) { - return parentBindingContext.createChildContext(ko.utils.unwrapObservable(arrayValue)); + var createInnerBindingContext = function(arrayValue, index) { + var result = parentBindingContext.createChildContext(ko.utils.unwrapObservable(arrayValue)); + result['$index'] = index; + return result; }; // This will be called whenever setDomNodeChildrenFromArrayMapping has added nodes to targetNode @@ -139,10 +141,10 @@ return options['includeDestroyed'] || !ko.utils.unwrapObservable(item['_destroy']); }); - ko.utils.setDomNodeChildrenFromArrayMapping(targetNode, filteredArray, function (arrayValue) { + ko.utils.setDomNodeChildrenFromArrayMapping(targetNode, filteredArray, function (arrayValue, index) { // Support selecting template as a function of the data being rendered var templateName = typeof(template) == 'function' ? template(arrayValue) : template; - return executeTemplate(null, "ignoreTargetNode", templateName, createInnerBindingContext(arrayValue), options); + return executeTemplate(null, "ignoreTargetNode", templateName, createInnerBindingContext(arrayValue, index), options); }, options, activateBindingsCallback); }, null, { 'disposeWhenNodeIsRemoved': targetNode }); @@ -223,4 +225,4 @@ })(); ko.exportSymbol('ko.setTemplateEngine', ko.setTemplateEngine); -ko.exportSymbol('ko.renderTemplate', ko.renderTemplate); \ No newline at end of file +ko.exportSymbol('ko.renderTemplate', ko.renderTemplate);