in src/bindingHandlers/fastForeach.ts [212:259]
FastForEach.prototype.added = function (changeItem) {
const index = changeItem.index;
const valuesToAdd = changeItem.isBatch ? changeItem.values : [changeItem.value];
const referenceElement = this.getLastNodeBeforeIndex(index);
// gather all childnodes for a possible batch insertion
const allChildNodes = [];
for (let i = 0, len = valuesToAdd.length; i < len; ++i) {
let childNodes;
// we check if we have a pending delete with reusable nodesets for this data, and if yes, we reuse one nodeset
const pendingDelete = this.getPendingDeleteFor(valuesToAdd[i]);
if (pendingDelete && pendingDelete.nodesets.length) {
childNodes = pendingDelete.nodesets.pop();
} else {
const templateClone = this.templateNode.cloneNode(true);
let childContext;
if (this.noContext) {
childContext = this.$context.extend({
$item: valuesToAdd[i],
$index: this.noIndex ? undefined : ko.observable()
});
} else {
childContext = this.$context.createChildContext(valuesToAdd[i], this.as || null, this.noIndex ? undefined : extendWithIndex);
}
// apply bindings first, and then process child nodes, because bindings can add childnodes
ko.applyBindingsToDescendants(childContext, templateClone);
childNodes = ko.virtualElements.childNodes(templateClone);
}
// Note discussion at https://github.com/angular/angular.js/issues/7851
allChildNodes.push.apply(allChildNodes, Array.prototype.slice.call(childNodes));
this.firstLastNodesList.splice(index + i, 0, { first: childNodes[0], last: childNodes[childNodes.length - 1] });
}
if (typeof this.afterAdd === "function") {
this.afterAdd({
nodeOrArrayInserted: this.insertAllAfter(allChildNodes, referenceElement),
foreachInstance: this
}
);
} else {
this.insertAllAfter(allChildNodes, referenceElement);
}
};