JSFiddle - React, Tailwind, and code Playground
HTML
<script src="https://ajax.aspnetcdn.com/ajax/knockout/knockout-2.1.0.debug.js"></script>
<p data-bind="text: timesComputed"></p>
<button data-bind="click: more">MORE</button>
<ul data-bind="template: { name: 'items-template', foreach: items }">
</ul>
<script id="items-template">
<li>
<p data-bind="text: text"></p>
<ul data-bind="template: { name: 'subitems-template', foreach: subItems }">
</ul>
</li>
</script>
<script id="subitems-template">
<li>
<p data-bind="text: text"></p>
</li>
</script>
<textarea rows="20" cols="100" id="log"></textarea>
<button id="clear">Clear</button>
CSS
ul > li > ul > li { margin-left: 10px; }
JavaScript
var subItemIndex = 0;
$("#clear").on("click", function () {
$("#log").empty();
});
function log(msg) {
$("#log").text(function (_, current) {
return current + "\n" + msg;
});
}
function Item(num, root) {
var idx = 0;
this.text = ko.observable("Item " + num);
this.subItems = ko.observableArray([]);
this.addSubItem = function () {
this.subItems.push(new SubItem(++subItemIndex, root));
}.bind(this);
this.addSubItem();
this.addSubItem();
this.addSubItem();
}
function SubItem(num, root) {
this.text = ko.observable("SubItem " + num);
this.computed = ko.computed(function () {
log("computing for " + this.text());
return root.text();
}, this);
this.computed.subscribe(function () {
root.timesComputed(root.timesComputed() + 1);
}, this);
}
function Root() {
var i = 0;
this.items = ko.observableArray([]);
this.addItem = function () {
this.items.push(new Item(++i, this));
}.bind(this);
this.text = ko.observable("More clicked: ");
this.timesComputed = ko.observable(0);
this.more = function () {
this.items.removeAll();
this.addItem();
this.addItem();
this.addItem();
this.timesComputed(0);
this.text("More clicked " + i);
}.bind(this);
this.more();
}
var vm = new Root();
ko.applyBindings(vm);