A tree view control that reads and writes a list of business objects with a parent reference (flat list, the usual database shape) - selecting a node exposes it for editing, changes go back into the same list. Complete custom coach view:
// Coach view "TreeEdit"
// binding: list of Node { id: String, parentId: String, label: String, data: ANY } configuration option: selected (Node, output) boundary event on select
// Layout (HTML): <div class="tree"></div>
// load handler
var _this = this, root = this.context.element.querySelector(".tree");
_this.render = function () {
var items = _this.context.binding.get("value"); items = items && items.items ? items.items : (items || []);
var byParent = {};
items.forEach(function (n) { (byParent[n.parentId || ""] = byParent[n.parentId || ""] || []).push(n); });
root.innerHTML = "";
(function build(parentId, container) {
var ul = document.createElement("ul"); container.appendChild(ul);
(byParent[parentId] || []).forEach(function (n) {
var li = document.createElement("li"), span = document.createElement("span");
var kids = byParent[n.id] || [];
span.textContent = (kids.length ? "▸ " : "• ") + n.label; span.className = "node"; li.appendChild(span);
span.onclick = function (e) {
e.stopPropagation();
root.querySelectorAll(".node.sel").forEach(function (x) { x.classList.remove("sel"); }); span.classList.add("sel");
_this.context.options.selected.set("value", n); // the selected node (same object as in the list) -> bound coach controls edit it
var sub = li.querySelector("ul"); if (sub) sub.style.display = sub.style.display === "none" ? "" : "none";
_this.context.trigger(); // boundary event if the CSHS wants to react
};
if (kids.length) build(n.id, li);
ul.appendChild(li);
});
})("", root);
};
_this.render();
// change handler (list changed, e.g. label edited in a bound Text control): _this.render();On the coach: the TreeEdit view bound to tw.local.nodes, its selected option bound to tw.local.current, and a small form (Text for tw.local.current.label, other fields) - because current references the element of the list, edits in the form change the list; the tree re-renders on its change event. Add / delete nodes with buttons that push into / splice the list (push into the bound list, then call the view's render()). Persist by submitting the coach (the list is the variable) or with a service flow. Styling: 15 lines of CSS in a managed asset (.tree ul { list-style: none; padding-left: 16px } .node.sel { background: #d9edf7 }).
References