Knockout.js + JSTree
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.0.9/jstree.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.0.9/themes/default/style.min.css">
<div id="navigation">
<ul class="treeview" data-bind="foreach: { data: partTypes}">
<li>
<a href="#" data-bind="text:name"></a>
</li>
</ul>
</div>
JavaScript
$(document).ready(function() {
function treeNode(name) {
console.log(name);
var self = this;
self.name = ko.observable(name);
}
function partsViewModel() {
var self = this;
self.partTypes = ko.observableArray([]);
self.selectedPartType = ko.observable();
$.getJSON('/echo/jsonp', {
home: 'Home',
about:'About Me'
}, function(allData) {
var mappedPartTypes = $.map(allData, function(item) {
return new treeNode(item);
});
self.partTypes(mappedPartTypes);
self.redrawTree();
});
self.redrawTree = function() {
console.log('redraw');
$('#navigation').jstree({
'themes': {
'theme': 'classic'
},
'contextmenu': {
'items': function($node) {
var tree = $('#navigation').jstree(true);
return {
'Create': {
'separator_before': false,
'separator_after': false,
'label': 'Create',
'action': function(obj) {
$node = tree.create_node($node);
tree.edit($node);
}
},
'Rename': {
'separator_before': false,
'separator_after': false,
'label': 'Rename',
'action': function(obj) {
console.log($node);
tree.edit($node);
}
},
'Remove': {
'separator_before': false,
'separator_after': false,
'label': 'Remove',
'action': function(obj) {
tree.delete_node($node);
}
}
};
}
},
'plugins': ['contextmenu']
});
};
}
ko.applyBindings(new partsViewModel());
});