Create a treeview that accepts an observable array as input

1

Hello,

I need to create a treeview that accepts an observable as input so that as I remove / add a node in the tree it updates itself. I've been researching and many people use the biding template to do this however, I'm still kind of confused about their use.

Below is my code attempt. In it I only print the first element of the tree and not its children.

var ViewModel = function(){
	self = this;
	self.data = ko.observable(data);
   	
  debugger;
}

var data = {
    items: [{
        "name": "MORPHED",
        "items": [{
            "name": "5 Day",
            "items": [{
                "CategoryId": 20,
                "name": "30 day countdown"
            }, {
                "CategoryId": 19,
                "name": "Staffing your program"
            }, {
                "CategoryId": 22,
                "name": "Emergency/Medical Information"
            }, {
                "CategoryId": 18,
                "name": "Promoting your program"
            }, {
                "CategoryId": 21,
                "name": "Week of camp"
            }]
        }, {
            "name": "4 Day",
            "items": []
        }, {
            "name": "1/2 Day",
            "items": []
        }, {
            "name": "Age Targeted",
            "items": []
        }]
    }, {
        "name": "CREATE",
        "items": [{
            "name": "5 Day",
            "items": []
        }, {
            "name": "4 Day",
            "items": []
        }, {
            "name": "1/2 Day",
            "items": []
        }]
    }, {
        "name": "INNOVATE",
        "items": [{
            "name": "5 Day",
            "items": []
        }, {
            "name": "4 Day",
            "items": []
        }, {
            "name": "1/2 Day",
            "items": []
        }]
    }, {
        "name": "ENVISION",
        "items": [{
            "name": "5 Day",
            "items": []
        }, {
            "name": "4 Day",
            "items": []
        }, {
            "name": "1/2 Day",
            "items": []
        }]
    }]
};

var viewModel = new ViewModel();
ko.applyBindings(viewModel);
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script><uldata-bind="template: { name: 'itemTmpl', foreach: $root.data().items }"></ul>

<script id="itemTmpl" type="text/html">
    <li>
        <span data-bind="text: name"></span>
        <ul data-bind="template: { name: 'itemTmpl', foreach: $root.data().items }">
        </ul>
    </li>
</script>

Here follows the example I'm basing.

    
asked by anonymous 20.04.2016 / 14:12

1 answer

1

Your question can be divided into two. The first one refers to showing the tree, with a recursive template. The second refers to performing editing / adding / deleting operations on items in the tree.

1. Recursion

To use the template (without the add / remove functions) you first need to know where your templates are pointing:

The code below does not work because you're pointing to data() as a function, it's just the name of your variable.

     <ul data-bind="template: { name: 'itemTmpl', foreach: $root.data().items }"></ul>

For the first level of the tree you only need to point to the properties of the model you want to display, in this case, items , like this:

     <ul data-bind="template: { name: 'itemTmpl', foreach: $root.items }"></ul>

Or so (since the first level is already $root ):

     <ul data-bind="template: { name: 'itemTmpl', foreach: items }"></ul>

Now in the levels below, since you are no longer in $root , you must reference the current item with $data.items in foreach :

<script id="itemTmpl" type="text/html">
    <li>
        <span data-bind="text: name"></span>
        <ul data-bind="template: { name: 'itemTmpl', foreach: $data.items }">
        </ul>
    </li>
</script>

So your recursion is already done.

Fiddle

var data = {
    items: [{
        "name": "MORPHED",
        "items": [{
            "name": "5 Day",
            "items": [{
                "CategoryId": 20,
                "name": "30 day countdown"
            }, {
                "CategoryId": 19,
                "name": "Staffing your program"
            }, {
                "CategoryId": 22,
                "name": "Emergency/Medical Information"
            }, {
                "CategoryId": 18,
                "name": "Promoting your program"
            }, {
                "CategoryId": 21,
                "name": "Week of camp"
            }]
        }, {
            "name": "4 Day",
            "items": []
        }, {
            "name": "1/2 Day",
            "items": []
        }, {
            "name": "Age Targeted",
            "items": []
        }]
    }, {
        "name": "CREATE",
        "items": [{
            "name": "5 Day",
            "items": []
        }, {
            "name": "4 Day",
            "items": []
        }, {
            "name": "1/2 Day",
            "items": []
        }]
    }, {
        "name": "INNOVATE",
        "items": [{
            "name": "5 Day",
            "items": []
        }, {
            "name": "4 Day",
            "items": []
        }, {
            "name": "1/2 Day",
            "items": []
        }]
    }, {
        "name": "ENVISION",
        "items": [{
            "name": "5 Day",
            "items": []
        }, {
            "name": "4 Day",
            "items": []
        }, {
            "name": "1/2 Day",
            "items": []
        }]
    }]
};
 
ko.applyBindings(data);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script><uldata-bind="template: { name: 'itemTmpl', foreach: items }"></ul>

<script id="itemTmpl" type="text/html">
    <li>
        <span data-bind="text: name"></span>
        <ul data-bind="template: { name: 'itemTmpl', foreach: $data.items }">
        </ul>
    </li>
</script>

2. Operation on items

The second problem is a bit more complex, since you need your array to be observable as a whole. A observableArray allows added or removed items to be automatically reflected in your array, so you need a template that contains those operations. To avoid verbosity, I'll post the minimum for this set of operations:

Template working with required operations: link

Template with data preloaded through recursion: link

    
13.07.2016 / 02:07