I'm currently developing a layout engine using knockout as a base, but I've run into a bit of a snag. (sorry for the long post)
I'm using Knockout 3.5.1.
I have the following index.html:
<body>
<toolbar-dock ui-name="toolbar"></toolbar-dock>
<general-layout ui-name="main" params="resize: true">
<!-- this one is transparent, and overlays ontop of the main screen. -->
<general-dock ui-name="ui" params="resize: 'fit'" id="hud_overlay"></general-dock>
</general-layout>
<main-statusbar></main-statusbar>
</body>
general-dock has the following .html:
<horizontal-dock>
<vertical-dock ui-name="left"></vertical-dock>
<vertical-dock ui-name="middle">
<horizontal-dock ui-name="top"></horizontal-dock>
<content-dock ui-name="middle">
</content-dock>
<horizontal-dock ui-name="bottom"></horizontal-dock>
</vertical-dock>
<vertical-dock ui-name="right" ></vertical-dock>
</horizontal-dock>
The UIManager.registerDockType function registers a dock with knockout with the following parameters:
- ComponentName: The first parameter is the name of the custom element, which is used to register with knockout.
- DockClass: The second parameter is instantiated and passed the element.
- TemplateString: The third parameter is the template string provided to the knockout class.
The function effectively is this:
ko.components.register(ComponentName, {
template: TemplateString,
viewModel: {
createViewModel: (params, componentInfo) => {
if (!componentInfo) {
throw new Error("Component didn't initialise correctly.");
}
var context = ko.contextFor(componentInfo.element);
new DockClass(componentInfo.element);
return context.$data;
}
}
})
And is called like this:
UIManager.registerDockType("vertical-dock", VerticalDock, "<!-- ko template: { nodes: $componentTemplateNodes } --><!-- /ko -->");
UIManager.registerDockType("horizontal-dock", HorizontalDock, "<!-- ko template: { nodes: $componentTemplateNodes } --><!-- /ko -->");
UIManager.registerDockType("content-dock", CenterDock, "<!-- ko template: { nodes: $componentTemplateNodes } --><!-- /ko -->");
I expected the following HTML to be output:
<body>
<toolbar-dock ui-name="toolbar"></toolbar-dock>
<general-layout ui-name="main" params="resize: true">
<horizontal-dock>
<vertical-dock ui-name="left"></vertical-dock>
<vertical-dock ui-name="middle">
<horizontal-dock ui-name="top"></horizontal-dock>
<content-dock ui-name="middle">
<!-- There should be an error, blank entry or infinite duplication at this point because $componentTemplateNodes is now exhausted? -->
</content-dock>
<horizontal-dock ui-name="bottom"></horizontal-dock>
</vertical-dock>
<vertical-dock ui-name="right" ></vertical-dock>
</horizontal-dock>
</general-layout>
<main-statusbar></main-statusbar>
</body>
But what I'm getting is this:
<body>
<toolbar-dock ui-name="toolbar"></toolbar-dock>
<general-layout ui-name="main" params="resize: true">
<horizontal-dock>
<!-- ko template: { nodes: $componentTemplateNodes } -->
<!-- this one is transparent, and overlays ontop of the main screen. -->
<general-dock ui-name="ui" params="resize: 'fit'" id="hud_overlay"></general-dock>
<!-- /ko -->
</horizontal-dock>
</general-layout>
<main-statusbar></main-statusbar>
</body>
Why does this happen? Why is it that within the horizontal-dock I'm getting the $componentTemplateNodes of the general-layout?
When I was using knockout 3.4.0 and highlighting the <!-- ko template
node using knockout-inspector it was showing the $componentTemplateNodes that I was expecting, however the DOM actually had the parent's $componentTemplateNodes. After upgrading to 3.5.0 knockout-inspector and the DOM are showing the same thing (although not what I expected).
What I expected is that (like with $data) a new $componentTemplateNodes is created every time a knockout controlled template node is entered if that template node has children. And that when recursing down the dom each child that fulfilled that criteria would replace the current $componentTemplateNodes with it's own $componentTemplateNodes.
This would mean that the $componentTemplateNodes for general-dock
would be:
<!-- this one is transparent, and overlays ontop of the main screen. -->
<general-dock ui-name="ui" params="resize: 'fit'" id="hud_overlay"></general-dock>
But once we get to the horizontal-dock
when knockout is expanding the general-dock it would create a new $componentTemplateNodes value (because it has children) containing the following representation:
<vertical-dock ui-name="left"></vertical-dock>
<vertical-dock ui-name="middle">
<horizontal-dock ui-name="top"></horizontal-dock>
<content-dock ui-name="middle">
</content-dock>
<horizontal-dock ui-name="bottom"></horizontal-dock>
</vertical-dock>
<vertical-dock ui-name="right" ></vertical-dock>
I expected that the only way to get back to the original $componentTemplateNodes would be to either recurse up $parentContext or pass them down as a parameter.
Is this a bug or am I doing something stupid?