Skip to content

Instantly share code, notes, and snippets.

@seiyria
Last active April 2, 2018 03:42
Show Gist options
  • Save seiyria/8044769 to your computer and use it in GitHub Desktop.
Save seiyria/8044769 to your computer and use it in GitHub Desktop.
A custom, collapsible navigation tree using knockout.js (and knockout.mapping.js), Bootstrap 3, and glyphicons. Example picture: http://puu.sh/5RWjW.png, fiddle here: http://jsfiddle.net/Ar88e/1/
#nav-bar ul {
list-style-type: none;
}
#nav-bar {
padding-left: 0;
}
#nav-bar > ul {
padding-left: 20px;
}
.tree-item {
font-family: 'Glyphicons Halflings';
-webkit-font-smoothing: antialiased;
font-size: 75%;
}
.pointer-icon {
cursor: pointer;
}
<script type="text/html" id="tree-template">
<ul data-bind="foreach: nodes">
<li>
<span data-bind="
template: { name: 'node-name-template', data: $data },
css: { 'pointer-icon': nodes().length > 0 }"></span>
<div data-bind="template: { name: 'folder-template', data: $data }, visible: isExpanded"></div>
</li>
</ul>
</script>
<script type="text/html" id="folder-template">
<ul data-bind="foreach: nodes">
<li>
<div data-bind="template: { name: 'node-template', data: $data }"></div>
</li>
</ul>
</script>
<script type="text/html" id="node-template">
<span data-bind="
template: { name: 'node-name-template', data: $data },
css: { 'pointer-icon': nodes().length > 0 }"></span>
<!-- ko if: nodes().length !== 0 -->
<div data-bind="template: { name: 'folder-template', data: $data }, visible: isExpanded"></div>
<!-- /ko -->
</script>
<script type="text/html" id="node-name-template">
<span class="tree-item" data-bind="
css: {
'glyphicon glyphicon-minus-sign': isExpanded() && nodes().length > 0,
'glyphicon glyphicon-plus-sign': !isExpanded() && nodes().length > 0,
'glyphicon glyphicon-stop': nodes().length === 0
}
"></span>
<span data-bind="
text: name,
attr: { 'title': description },
tooltip: { delay: { show: 500, hide: 10 } },
click: toggleVisibility"></span>
</script>
ko.bindingHandlers.tooltip = {
init: function(element, valueAccessor) {
var local = ko.utils.unwrapObservable(valueAccessor()),
options = {};
ko.utils.extend(options, local);
$(element).tooltip(options);
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).tooltip("destroy");
});
}
};
var NodeModel = function(data) {
var self = this;
self.isExpanded = ko.observable(true);
self.description = ko.observable();
self.name = ko.observable();
self.nodes = ko.observableArray([]);
self.toggleVisibility = function() {
self.isExpanded(!self.isExpanded());
};
ko.mapping.fromJS(data, self.mapOptions, self);
};
NodeModel.prototype.mapOptions = {
nodes: {
create: function(args) {
return new NodeModel(args.data);
}
}
};
var __sampleData = {
navTree: {
nodes: [
{
name: 'Root 1',
description: 'test description!',
objectId: '',
nodes: [
{
name: 'Child 1',
description: 'test description!',
objectId: '',
nodes: [
]
},
{
name: 'Child 2',
description: 'test description! asd',
objectId: '',
nodes: [
{
name: 'Child Child 1',
description: 'test description!',
objectId: '',
nodes: [
]
}
]
}
]
},
{
name: 'Root 2',
description: 'this is a longer description and it is still fabulous',
objectId: '',
nodes: [
]
},
{
name: 'Root 3',
description: '',
objectId: '',
nodes: [
]
}
]
}
};
<div id="nav-bar" data-bind="template: { name: 'tree-template', data: treeData }"></div>
var PageModel = function() {
var self = this;
self.treeData = ko.observable();
self.loadData = function(data) {
self.treeData(new NodeModel(data.navTree));
};
}
var pageModel = new PageModel();
pageModel.loadData(__sampleData);
ko.applyBindings(pageModel);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment