Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

knockout.js recursive binding

I'm trying to do some complex binding with knockout (at least for a newbie like me).

Consider the following data:

var originalData = {
id: 1,
name: "Main",
children: [ { id: 2, name: "bob", children: []}, { id: 3, name: "ted", children: [{id: 5, name:"albert"}, {id: 9, name: "fred"}]} ],
selectedChild:  { id: 2, name: "bob" }
};

<table>
<tr>
    <td data-bind="text: name"></td>
</tr>
<tr data-bind="if: children().length > 0">
    <td>
        <select data-bind="options: children,
            optionsText: function(item){
                return item.name;
                    }, 
            optionsCaption: 'Choose...'"></select>       
    </td>
</tr>

Ok, that was the easy part.

The hard part, is that whenever an item is selected in the list, if this item has children then a new select box should appear underneath. Its datasource would be the children of the selected item in the first select box. Of course, it could continue on with any level of deepness.

How should I solve this problem with knockout ?

I've put together a sample of what I have so far on jsfiddle: http://jsfiddle.net/graphicsxp/qXZjM/

like image 241
Sam Avatar asked Mar 20 '13 18:03

Sam


1 Answers

You can use recursive templates in knockout by putting the template into a script tag. Templates in a script tag can reference themselves, like this:

<div data-bind="template: 'personTemplate'"></div>

<script type="text/ko" id="personTemplate">
    <span data-bind="text: name"></span>
    <select data-bind="options: children, optionsText: 'name', optionsCaption: 'Choose',  value: selectedChild"></select>
    <!-- ko if: selectedChild -->
    <div data-bind="template: { name: 'personTemplate', data: selectedChild }"></div>
    <!-- /ko -->
</script>

Here is the fiddle


Update:

You can use a computed to easily do this, and remove the logic from the view (which I think is better in this case), and then bind the if to it.

self.showChildren = ko.computed(function() {
    return self.selectedChild()
        && self.selectedChild().children().length > 0;
});

If you want to put both in the if block, you can, you just need to include the parens. The reason for this is that observables are functions; knockout lets you exclude them if you are just using the single reference, but they are required to "drill down" to their properties.

if: selectedChild() && selectedChild().children().length > 0

Here is the updated fiddle

like image 52
Kyeotic Avatar answered Jan 03 '23 12:01

Kyeotic