existing content mapped to observableArray

https://groups.google.com/d/topic/knockoutjs/OJamQFg3Y9c/discussion

by rniemeyer

HTML

<script src="http://knockoutjs.com/downloads/knockout-1.3.0beta.debug.js"></script>
<ul id="test" data-bind="templateFromExisting: { foreach: items }">
    <li>
        <input value="one" data-bind="value: name" />
        <a href="#" class="remove">delete</a>
    </li>
    <li>
        <input value="two" data-bind="value: name" />
        <a href="#" class="remove">delete</a>
    </li>
    <li>
        <input value="three" data-bind="value: name" />
        <a href="#" class="remove">delete</a>
    </li>        
</ul>

<button data-bind="click: addItem">Add Item</button>

<hr/>

<div data-bind="text: ko.toJSON(viewModel)"></div>

CSS

a { font-size: .75em; }
.selected { background-color: #CCC; }

JavaScript

ko.bindingHandlers.templateFromExisting = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        //Use first element as our template
        var bindingValue = ko.utils.unwrapObservable(valueAccessor());
        if ((typeof bindingValue != "string") && (!bindingValue.name) && (element.nodeType == 1)) {
            // It's an anonymous template - store the element contents
            var firstElement = $(element).children()[0],
                firstElementHTML = firstElement.outerHTML || $(firstElement).clone().wrap("<div></div>").parent().html();
            new ko.templateSources.anonymousTemplate(element).text(firstElementHTML);
        }
        
        //TODO: consider "data" or viewModel when not using foreach
        var items = ko.utils.unwrapObservable(bindingValue.foreach);
        
        //generate a mapping from the existing elements
        var mappingResult = [];
        
        for (var i = 0, j = items.length; i < j; i++) {
            var mapping = {},
                child = $(element).children()[i];
                
            mapping.arrayEntry = items[i];
            mapping.domNodes = [child];
            mapping.dependentObservable = ko.dependentObservable(function() { } );
            //bind this node and its children
            ko.applyBindings(items[i], child);
            
            mappingResult.push(mapping);
        }
        
        console.log("lastMapping", mappingResult);
        
        ko.utils.domData.set(element, "setDomNodeChildrenFromArrayMapping_lastMappingResult", mappingResult);
        
        return { 'controlsDescendantBindings': true };
    },
    update: ko.bindingHandlers.template.update
};

function Item(id, name) {
    this.id = ko.observable(id);
    this.name = ko.observable(name);
}

var viewModel = {
    items: ko.observableArray([
        new Item(1, "one"),
        new Item(2, "two"),
        new Item(3, "three")
    ]),
    addItem: function() {
  ...