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));