Recursive template in KnockoutJS
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul data-bind="template: { name: 'item-template', foreach: $root.subitemsOf(null) }"></ul>
<script type="text/html" id="item-template">
<li>
<span data-bind="text:label"></span>
<!-- ko if: $root.hasSubitems($data) -->
<ul data-bind="template: {name: 'item-template', foreach: $root.subitemsOf($data)}"></ul>
<!-- /ko -->
</li>
</script>
JavaScript
function ItemModel(id, parent_id, label) {
var self = this;
self.id = ko.observable(id);
self.parentId = ko.observable(parent_id);
self.label = ko.observable(label);
}
function RecursiveListViewModel(tasks) {
var self = this;
self.items = ko.observableArray(tasks);
self.subitemsOf = function (item){
var children = ko.utils.arrayFilter(self.items(), function (arrayItem) {
var parentItemId = (null === item) ? null : item.id();
return arrayItem.parentId() == parentItemId;
});
return children;
}
self.hasSubitems = function (item) {
var firstMatch = ko.utils.arrayFirst(self.items(), function (arrayItem) {
return (arrayItem.parentId() == item.id());
});
return (null !== firstMatch); // At least one item found in array
};
}
var Items = [
new ItemModel(1, null, 'Item-1'),
new ItemModel(2, null, 'Item-2'),
new ItemModel(3, 1, 'Item-1-1'),
new ItemModel(4, 2, 'Item-2-1'),
];
ko.applyBindings(new RecursiveListViewModel(Items));