Is there a way to configure a Knockout component to replace the container element instead of nesting its content inside the container element?
For example, if I have a custom component registered as my-custom-element
with the following template:
<tr>
<p>Hello world!</p>
</tr>
Is it possible to use the component like this:
<table>
<tbody>
<my-custom-element></my-custom-element>
</tbody>
</table>
And have the final product be this:
<table>
<tbody>
<tr>
<p>Hello world!</p>
</tr>
</tbody>
</table>
Instead of this: (the way Knockout renders components by default)
<table>
<tbody>
<my-custom-element>
<tr>
<p>Hello world!</p>
</tr>
</my-custom-element>
</tbody>
</table>
Based on the answer to this question, it seems that this functionality is built into the templating engine, which I'm assuming is also used when rendering component templates.
Is there a way to specify that a component should be rendered with a renderMode
of replaceNode
?
I'm aware of the "virtual element" syntax, which allows components to be defined inside an HTML comment:
<table>
<tbody>
<!--ko component { name: 'my-custom-element' }--><!--/ko-->
</tbody>
</table>
but I really dislike this syntax - writing real code inside a comment feels like a dirty, dirty hack.
You can use the comment tags:
https://knockoutjs.com/documentation/component-binding.html#note-using-component-without-a-container-element
I imagine the lack of this option could be defended as such, considering the nature of the library (there, I said it), and the developer's team philosophy:
Knockout is a library and unlike other MVC's it does not force you to use a framework-defined way to structure your application. If you consider the template engine in Knockout vs. virtually all other JS templating engines (in Angular, underscore, mustache, etc.), Knockout comes out as the only one not 'modding' native HTML5 rendering. All others use a custom tag, be it
<% %>
or{{ }}
which requires a small JS parser to transform the tags into something meaningful (now KO also has a Knockout punches plugin which includes the mustache-style tags, and admittedly KO does 'sin' a small bit with<!-- ko -->
comments). KO instead uses HTML 5 custom elements, comment and attribute tags, completely "vanilla".For instance, the JS/DOM type of 'object (and real life?) hierarchy' is used: only parents can ever exerce power on their children, and so the bound-to element is not replaced, but enlarged with children. For illustration:
Subsequently, the preferred way of data-binding with KO, illustrated well by the
foreach
binding, is:The previous snippet being an HTML representation of a JS array, the hierarchy is visible again:
This leads to your HTML view being a perfect replication of your JS data (and it would be interesting to see someone build a tool that shows the data-bind indentation levels (with
with
andforeach
) in an HTML document. In some cases however, you need comment tags, so as to not break the lay-out of your HTML, or your css rules (nesting), for example a 'text-only' component (i18n) that is to be injected between text nodes:Or when you don't want an empty element to take up space when hidden
And then there are custom tags, which are standardized and a hell of a lot clearer; but unfortunately not a 100% ready yet. Choose
data-bind
first,<!-- ko -->
second, and<custom>
third (would be higher if fully implemented). That's how I see it, anyway. As for your specific case, if your component holds a list model, you could do:And include the
tbody
in your VM, else if it is a listitem model, you can use either one of the three 'syntaxes', eg the comment syntaxOr decouple your component completely from the requirement of being nested inside a specific parent (table), adhering to the SOC principle, eg:
or with a custom tag:
in that order of preference..
Good News! In Knockout 3.3.0 they just introduced the concept of Passing markup into components. This is done by using the
$componentTemplateNodes
template inside your component.From the linked example: