Nested template

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

by gurkavcu

HTML

<script src="http://github.com/downloads/SteveSanderson/knockout/jquery.tmpl.js"></script>
<script src="https://github.com/SteveSanderson/knockout/raw/master/build/output/knockout-latest.debug.js"></script>
<ul data-bind="template: { name: 'nodeTmpl', foreach: nodes }"></ul>

<script id="nodeTmpl" type="text/html">
    <li>
        <div data-bind="text: name"></div>
        <ul data-bind="template: { name: 'nodeTmpl', foreach: children }"></ul>
    </li>
</script>
<br/><br/>
<a id="call" href="#" >Add New Node</a>

CSS

ul { margin-left: 10px; }

JavaScript

var dataFromServer = [
    {
    name: "root",
    parent: null},
{
    name: "a",
    parent: "root"},
{
    name: "a1",
    parent: "a"},
{
    name: "a2",
    parent: "a"},
{
    name: "b",
    parent: "root"},
{
    name: "b1",
    parent: "b"},
{
    name: "b1a",
    parent: "b1"}
];

function Node(name, children) {
    this.name = ko.observable(name);
    this.children = ko.observableArray(children || []);
}

function getMappedData(data) {
    var result = [];
    var index = {};

    //build an index of Node objects to avoid looping, unless you can depend on the order always having parents first in the data
    ko.utils.arrayForEach(data, function(node) {
        index[node.name] = new Node(node.name);
    });

    //assign nodes to their parents, if their is no parent assign it to the resulting array
    ko.utils.arrayForEach(data, function(node) {
        if (node.parent) {
            index[node.parent].children.push(index[node.name]);
        } else {
            result.push(index[node.name]);
        }
    });
    return result;
}

var viewModel = {
    nodes: ko.observableArray(getMappedData(dataFromServer))
};


ko.applyBindings(viewModel);

$(document).ready(function() {
    updateNode = function() {
        
        var root = viewModel.nodes()[0];
        var bchilds = root.children()[1].children();
        var newNode = new Node('c');
        
        bchilds.push(newNode);
        console.log(ko.toJSON(viewModel));
        // ko.applyBindings(viewModel);
    };

    $('#call').click(updateNode);

});