Reputation: 3110
I have a nested HTML list and I need to dynamically load new item when I click to expand one node. I found and modified this snippet but when I try to push new nodes in my list I got an error:
Uncaught ReferenceError: Unable to parse bindings.
Bindings value: visible: expanded, click: toggle
Message: toggle is not defined
HTML
<!DOCTYPE html>
<html>
<head>
<link data-require="font-awesome@*" data-semver="3.0.2" rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/3.0.2/css/font-awesome.min.css" />
<script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script data-require="knockout@*" data-semver="2.3.0" src="http://knockoutjs.com/downloads/knockout-2.3.0.js"></script>
<script data-require="knockout.mapping@*" data-semver="2.3.5" src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.3.5/knockout.mapping.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
<script type="text/html" id="tree-node">
<li>
<span class="node-toggle" data-bind="visible: expanded, click: toggle">–</span>
<span class="node-toggle" data-bind="visible: collapsed, click: toggle">+</span>
<span class="node-label" data-bind="text: name, click: $root.selected"></span>
<div data-bind="if: expanded">
<ul data-bind="template: {name: 'tree-node', foreach: children}"></ul>
</div>
</li>
</script>
</head>
<body>
<h1>Example Tree</h1>
<p>Click on the plus sign to expand a node, click on the label to select it.</p>
<ul data-bind="template: {name: 'tree-node', data: root}"></ul>
<div data-bind="if: selected">
<div data-bind="with: selected">
Selected Node: <span data-bind="text: name"></span>
</div>
</div>
</body>
</html>
JS
// Code goes here
$(function() {
function TreeNode(values) {
var self = this;
ko.mapping.fromJS(values, { children: { create: createNode }}, this);
this.expanded = ko.observable(false);
this.collapsed = ko.computed(function() {
return !self.expanded();
})
}
TreeNode.prototype.toggle = function () {
this.expanded(!this.expanded());
if (this.expanded() && !this.children().length){
// This throw an error
this.children.push(TreeNode(
{id: "xxxx", name: "Node xxxx", children: []}
));
}
};
function createNode(options) {
return new TreeNode(options.data);
}
var root = new TreeNode({ id: "1", name: "Root", children: [
{ id: "1.1", name: "Node 1", children: [
{id: "1.1.1", name: "Node 1.1", children: []},
{id: "1.1.2", name: "Node 1.2", children: []}
]},
{ id: "1.2", name: "Node 2", children: []}
]});
var viewModel = {
root: root,
selected: ko.observable()
};
ko.applyBindings(viewModel, $('html')[0]);
});
CSS
/* Styles go here */
ul, li {
list-style: none;
}
.node-label {
cursor: pointer;
}
.node-toggle {
display: inline-block;
width: 1em;
cursor: pointer;
}
Ideally the solution should work for infinite nesting levels. Can anybody give me a hint?
Upvotes: 0
Views: 464
Reputation: 43899
TreeNode
is a constructor, but you are calling it without new
in the line
this.children.push(TreeNode(
{id: "xxxx", name: "Node xxxx", children: []}
));
This is just one of those gotchas you have to watch out for. If you use strict mode, then any use of this
in a constructor you didn't call with new
would be undefined
instead of the global object. That could help.
Upvotes: 1