Highlight recursive knockout pattern

In this example, I have a nested template for displaying a tree view, for example, indented, but the rendered HTML on the leaf nodes is not what I would expect. How to get leaf nodes to display the expected html and NOT contain a child div container?

If I put the if binding outside the template binding, I get a javascript error:

Uncaught Error: Multiple bindings (if and template) are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.

Note. Using the anchor comment if <!-- ko if: children().length > 0 --> works to remove the unwanted element, but the resulting html is riddled with comments, and I would prefer to keep it clean, since I could have potentially hundreds of leaf nodes.

I tried using 2 patterns, one with a container and one without, and nesting conditions for the name, but the template always displayed nodeTempl: data-bind="template: { name: (children().length > 0) ? 'nodeTempl' : 'leafTempl', foreach: children }"

I think I really want to apply another template based on the state of the child process that it processes.

Leaf node does it like

 <div> <div data-bind="text: name">Node 1-1-1</div> <div class="indent-1" data-bind="template: {if: children().length &gt; 0, name: 'nodeTempl', foreach: children }"></div> </div> 

But I want it to look like this:

 <div> <div data-bind="text: name">Node 1-1-1</div> </div> 

Here's the setting:

HTML

 <style type="text/css"> .indent-1 { margin-left: 20px; } </style> <script id="nodeTempl" type="text/html"> <div> <div data-bind="text: name"></div> <div class="indent-1" data-bind="template: {if: children().length > 0, name: 'nodeTempl', foreach: children }"></div> </div> </script> <div data-bind="template: { name: 'nodeTempl', foreach: children }"></div> 

Script

 var node = function (config) { var self = this; self.name = ko.observable(config.name); self.children = ko.observableArray([]); if ($.isArray(config.children)) { for (var i = 0; i < config.children.length; i++) { self.children.push(new node(config.children[i])); } } }; ko.applyBindings(new node( { name: 'root', children: [ { name: 'Node1', children: [{name: 'Node 1-1', children: [{name: 'Node 1-1-1'}]},{name: 'Node 1-2'}]}, {name: 'Node2',children: [{name: 'Node 2-1'},{name: 'Node 2-2'}]}, {name: 'Node3'}, ] } )); 

Output

 <div data-bind="template: { name: 'nodeTempl', foreach: children }"> <div> <div data-bind="text: name">Node1</div> <div class="indent-1" data-bind="template: {if: children().length &gt; 0, name: 'nodeTempl', foreach: children }"> <div> <div data-bind="text: name">Node 1-1</div> <div class="indent-1" data-bind="template: {if: children().length &gt; 0, name: 'nodeTempl', foreach: children }"> <div> <div data-bind="text: name">Node 1-1-1</div> <div class="indent-1" data-bind="template: {if: children().length &gt; 0, name: 'nodeTempl', foreach: children }"></div> </div> </div> </div> <div> <div data-bind="text: name">Node 1-2</div> <div class="indent-1" data-bind="template: {if: children().length &gt; 0, name: 'nodeTempl', foreach: children }"></div> </div> </div> </div> <div> <div data-bind="text: name">Node2</div> <div class="indent-1" data-bind="template: {if: children().length &gt; 0, name: 'nodeTempl', foreach: children }"> <div> <div data-bind="text: name">Node 2-1</div> <div class="indent-1" data-bind="template: {if: children().length &gt; 0, name: 'nodeTempl', foreach: children }"></div> </div> <div> <div data-bind="text: name">Node 2-2</div> <div class="indent-1" data-bind="template: {if: children().length &gt; 0, name: 'nodeTempl', foreach: children }"></div> </div> </div> </div> <div> <div data-bind="text: name">Node3</div> <div class="indent-1" data-bind="template: {if: children().length &gt; 0, name: 'nodeTempl', foreach: children }"></div> </div> </div> 
+4
source share
1 answer

You're on the right track, assuming you need two templates. The name template can be a function that returns the name of the template for each element of the array.

You can create a function like this:

 function nodeTemplate(node) { return node.children().length > 0 ? 'nodeTempl' : 'nodeLeafTempl'; } 

And change your mind to the following:

 <script id="nodeTempl" type="text/html"> <div> <div data-bind="text: name"></div> <div class="indent-1" data-bind="template: {name: nodeTemplate, foreach: children }"></div> </div> </script> <script id="nodeLeafTempl" type="text/html"> <div> <div data-bind="text: name"></div> </div> </script> <div data-bind="template: { name: nodeTemplate, foreach: children }"></div> 
+7
source

All Articles