Create a treeview that accepts an observable array as input

Asked

Viewed 63 times

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 the same update. I have been researching and many people use the biding template to do this however, I’m still a little confused about its use.

Below follows my attempt at code. In it I can 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>

<ul data-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.

1 answer

1


Your question can be divided into two. The first refers to showing the tree, with a recursive template. The second refers to performing edit/include/exclude operations on the tree.

1. Recursion

To use the template (without adding or removing functions) you first need to know where your templates are pointing to:

The code below does not work because you are pointing to data() as a function, being that it is only the name of its variable.

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

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

     <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, as you are no longer in the $root it is necessary to reference the current item with $data.items in the 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>

<ul data-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 in 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 will post the minimum for this set of operations:

Model operating with required operations: https://jsfiddle.net/claykaboom/m4vws8s2/

Template with data preloaded via recursion: https://jsfiddle.net/claykaboom/vb80r7xr/

  • 1

    Thank you so much, that’s what I was looking to do.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.