Before adding anything as complicated as https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Template-Instantiation.md it is better to ensure it improves the platform.
I'm especially interested in the performance aspect.
If a polyfill could implement the proposal without being significantly slower, would there be need to add the API natively?
Aside from the performance aspect, @surma is interesting it writing developer-facing documentation for this proposal, to lower the bar for feedback.
I spent last week implementing a polyfill for a syntax variation of the template instantiation proposal, and also ported a number of web components from the Elix library over to that polyfill to see whether template instantiation would make creating such components easier.
Upon reflection, it does not appear that template instantiation really helps us.
The web components we create are typically more complex than can be addressed with mustache syntax. We use a mixin architecture to create our components; e.g., a complex carousel component of ours is currently built from ~20 different mixins, each contributing some aspect of programmatic or interaction behavior to the component class.
Significantly, in this mixin architecture, the component class that defines the template _doesn't know_ what properties of sub-elements within that template will need to be modified by the mixins. Instead, a component asks itself (and therefore the mixins along its prototype chain) to build up a dictionary of all the updates that should be applied to the elements in its shadow tree. Then the component applies those updates.
Such separation of concerns means we cannot use the mustache syntax proposal, because that requires the complete set of attributes that will be modified to be expressed directly in the template.
One thing that might help would be a way for a component class to indicate that a collection of property updates be applied to a given element. Perhaps this would be something along the lines of React's spread syntax:
<div {...props}/>
We'd want to avoid introducing new syntax, so maybe we'd have a special property?
<div properties="{{props}}"></div>
I had a chance to talk about these experiments with @rniwa at Apple last week. During our conversation, I proposed adding something like the properties= idea above to Ryosuke. He suggested that, given our existing mixin architecture, what we'd really benefit from is something that lets us apply a collection of property updates to a _tree_ of elements.
I've gone ahead and written up that idea as a proposal for bulk property updates. (Filed as a separate issue.) That includes a hypothetical applyPropertiesById method that would achieve what Ryosuke suggested. With something like that, we wouldn't need the template instantiation proposal.
In any event, for the time being, we don't see template instantiation helping our web components library.
It's true that much (if not all) of what this API provides can be implemented in JS. For us, the main benefit of this proposal is that it paves a way to come up with a declarative syntax for custom elements. It also provides a useful mechanism for template libraries to update different parts of DOM easily. Finally, the default template processor provides a mechanism to easily create a template instance without having to import a third party library.
Hello, I'm new to this discussion, but I'd like to point out that there is no need for special syntax with curly braces to get useful things from templates. I'd like to point out two different template engines based on the same principles each that don't require any template syntax at all:
They both work on the same principles: You define your markup with absolutely no special syntax, custom attributes or anything like that. Then, you define a JSON object on the side that goes with your markup that is telling how to instantiate the markup for a given input data structure.
What I really like from this approach is that you get a clear separation between the markup and the data structure you want to template. From the beginning, web standards is all about separation of concerns, and I believe this approach fits very nicely with that.
@rniwa
The main problem with template instantiation proposal is that it attempts to solve a symptom, not provide the cure for the cause.
And there are two causes:
As all compromises, template instantiation proposal solves both poorly.
JS. For us, the main benefit of this proposal is that it paves a way to come up with a declarative syntax for custom elements.
It also provides a useful mechanism for template libraries to update different parts of DOM easily.
This "syntax" already exists, and has been battle proven over several years on thousands of web sites. As is the ability to efficiently and easily update different parts of the DOM.
And it looks more or less like this:
h('div',
{
attrs: { className: "x" },
props: { duration: y },
on: { click: () => z() }
},
[ h(...), h(...) ]
);
Yes. It's virtual DOM, popularised by React, and which exists as virtual-dom, hyperscript etc. There are differences in the APIs between these libraries, but they are quite superficial, and they more or less converged on the same set of principles:
f( tag, { props, attrs, events, lifecycle }, [children]) which is easy to reason about, easy to analyse, debug, optimiseMeanwhile templates... I don't know what actual issues do they solve? They introduce way more problems than it's worth:
is a new templating language:
// before
var X = template.getElementById("#part");
X.appendChild(document.createElement('span'));
X.appendChild(document.createTextNode('hello'));
// after
var X = template.getRequiredTemplatePartSomehow();
X.replace([document.createElement('span'), 'hello']);
The vast majority of the web isn't even using DOM APIs with Web Components, and goes straight for .innerHtml (even lit-html does that with reckless abandon). Given that the template proposal doesn't really add any new easy-to-use APIs, it will still be easier to drop to .innerHTML or reach for any of the virtual dom libraries out there than use the templates.
In my opinion, the templating proposal has its value to show some the problems that people experience with Web Components, but can never be a solution. The solution lies in better, easier-to-use declarative DOM APIs.
Hi @dmitriid
I don't follow why template instantiation could only be used inside a web component. It would be useful anytime there's repetitive markup that needs customizing in each instance. I encounter that scenario quite a bit, before and after web components.
The fundamental issue templates are trying to solve, as I understand it, is that repeatedly cloning a template for large chunks of easy to parse html is faster than making lots of appendChild or innerHTML calls, which can only be done after the expensive job of parsing the JavaScript. This is an empirical claim. Are you claiming otherwise? It would be great to show your counter-factual results.
HTML templates also feel much more "declarative" to me. Templates are inert, containing a data format (xml-ish) so they can be loaded quickly with no side effects. hyperscript may also have no side effects for simple examples (other than rendering the html of course), but you can evaluate any function you want during the processing, which could have unexpected side effects. To be fair, some aspects of the template instantiation proposal (which looks quite different from how you are describing it, with your before and after -- are we looking at the same proposal?) may also allow for functions with side effects, but I could be wrong.
For the record, I'm not opposed to introducing an h function into the api, if it fulfills some useful purpose, but I fail to see how it is easier to use than tagged template literals. Could you elaborate? The performance numbers I've seen comparing lit-html and hyperHTML, compared to react (and even preact) make me wonder what your objections are?
@bahrus
I do apologise if I sound terse or rude in the text below, as I'm writing this rather quickly in a spare moment (I wanted to acknowledge your response quickly, and not have you wait for a day or two).
I don't follow why template instantiation could only be used inside a web component. It would be useful anytime there's repetitive markup that needs customizing in each instance.
The whole discussion is mostly in the context of Custom Elements and Shadow DOM (see 2. Use Cases). The proposal itself is only limited to <template> elements.
That's why in my mind it was only limited to custom elements.
However, true, you can use them elsewhere:
rniwa = {name: "R. Niwa", email: "[email protected]"};
document.body.appendChild(contactTemplate.createInstance(rniwa));
This does still leave the question of how to more complex/nested templates where parts of a template are defined in other templates etc.
but you can evaluate any function you want during the processing, which could have unexpected side effects.
That is, side effects that are desired by the developer using it ;). There's no way to use a template that has this:
{{foreach items}}
<li class={{class}} data-value={{value}}>{{label}}</li>
{{/foreach}}
without first calling arbitrary functions to create these items and binding them to the template. And since the proposed way of creating such things is the same old DOM API with hardly any improvements, I fail to see the improvement.
And as you correctly mentioned, templates will need to be able to call arbitrary functions.
I fail to see how it is easier to use than tagged template literals. Could you elaborate? The performance numbers I've seen comparing lit-html and hyperHTML, compared to react (and even preact) make me wonder what your objections are?
The only reason lit-html works as it does is that dozens (hundreds?) of engineers spent hundreds of hours optimising to things that are frequently used and abused in JS:
.innerHtmlMore or less the only thing that lit-html does is parse a string at runtime, with regexps, concatenate it into an opaque string blob, and dump it into browser via .innerHtml, and let browser deal with it. (I have a separate rant on tagged template literals elsewhere).
It's not a good thing. It's a very bad thing, it only happens to work fast enough because browsers have had decades to optimise this (and ten years ago using .innerHtml was considered extremely bad practice see e.g. this StackOverflow comment, and even now .innerHtml is not optimised enough for certain cases, _and_ there are security considerations).
Meanwhile declarative DOM/virtual DOM libraries have to recreate the entire DOM model in memory, and manually diff it against the browser DOM.
The solution to all that (and to template instantiation) is definitely not, in my opinion:
<template> elements, and whose actual API is only marginally better than the rest of DOM APIs (and introduces a host of other problems, some of which.innerHTMLA declarative API (with, hopefully, browser-native DOM-diffing) solves a lot of the problems:
It may/will still be awkward to use, obviously. The next best thing, IMO, would be a standard/declarative way to create a DOM AST that you can pass to the browser. Virtual DOM libs, in essence, do that already. To quote Dan Abramov:
// JSX is a syntax sugar for these objects.
// <dialog>
// <button className="blue" />
// <button className="red" />
// </dialog>
{
type: 'dialog',
props: {
children: [{
type: 'button',
props: { className: 'blue' }
}, {
type: 'button',
props: { className: 'red' }
}]
}
}
Unfortunately, we cannot generate this AST for the browser and let it deal with it efficiently. We have to fall back to .innerHtml or hundreds of lines of tedious imperative code (or use libs/frameworks/wrappers).
With a natively supported declarative description of the DOM/AST you still have a low-level primitive that libs/frameworks can make even easier to use, but you can also trivially use it in vanilla JS code.
I do hope I made sense in the ramblings above :)
Thanks, @dmitriid, for your civil and non-rambling response :-)
I agree templates are a key part of the web component stack, and in retrospect you were replying to a message arguing why an (apparently) small library like what template instantiation would (apparently) entail should be built in to the platform, and the argument was made that it would benefit declarative custom elements.
But more generally, I for one have taken the liberty of using templates without web components on a number of occasions, and I don't think I'm alone.
I actually agree with you that the platform would benefit from some helper functions for those scenarios where a programmatic api is needed -- one that improves upon Object.assign, one that is specifically tailored for setting properties / attributes / events on a DOM element, i.e. a similar purpose to what you are laying out.
For example, I've been toying with a function I call "decorate", which I modeled after Vue/Polymer 1, but now realize, thanks to your bringing it to my attention, is quite similar to h.
The difference is that this decorate function is applied to an existing element, coming from a template or existing DOM tree, rather than being only useful for generating the HTML data structure itself.
I do think, given the wide range of libraries doing something similar, having something built in to the platform would be useful. Just my two cents. But so would template instantiation. I don't see why one precludes the other.
If a template api/syntax has built in functions to do certain things (like for-each), i.e. officially sanctioned functions, that is quite different from saying you can use any user-defined function you want. For a concrete example of what I'm getting at, I need to change the subject slightly: Take the github developers who are managing the github web site-. They are defining web components, which can enhance the markdown vocabulary. I recently realized I can use their custom elements in my markdown! Github only trusts that because they trust their own web components. They won't allow us to use arbitrary web components, for fairly obvious reasons.
I'm not saying user-defined functions must be forbidden in template languages, only that it is a significant line one is crossing, one which could be use to separate "non-declarative" vs "declarative" in a coherent (I think) way.
I suspect the author of hyperHTML would take issue with the statement that it required dozens or hundreds of engineers to match the performance of h based libraries :-).
Thanks for the link to your critique of tagged template literals. I'll take a look.
I suspect the author of hyperHTML would take issue with the statement that it required dozens or hundreds of engineers to match the performance of h based libraries
I think @dmitriid was referring to the fact these libraries (lit, lighter, or hyperHTML) are fast only because the primitives used have been made fast by browsers engineers.
I also think there's no shame in knowing, and using, fast primitives to deliver better UX, and that's naturally the goal of any performance oriented abstraction anyway 👋
@dmitriid, is that what you meant? If so, apologies for misinterpreting.
I guess I was thrown by the use of the word "to" in:
The only reason lit-html works as it does is that dozens (hundreds?) of engineers spent hundreds of hours optimising to things that are frequently used and abused in JS:
followed by his comment on what the browser engineers have done:
it only happens to work fast enough because browsers have had decades to optimise this.
I.e hundreds of lit-html engineers (I can't seem to locate his reference to hyperHTML, which line is that?) used bad practices, which happened to not matter because of the decades spent by browser engineers optimizing on those bad practices.
Apologies for my lack of reading comprehension.
I agree with you -- lit-html, lit, hyperHTML are fast and easy to use, great libraries, because they were built and optimized by a handful of dedicated and smart engineers, built on fast primitives built by great browser engineering teams. Somehow I didn't quite find that sentiment shared by @dmitriid, but what do I know? @dmitriid, we're all in agreement?
@dmitriid Neither template instantiation or lit-html use .innerHTML to update the DOM. This seems to be some FUD around how template instantiation works but template instantiation and lit-html actually work more efficiently than a virtual DOM by holding references to part of the DOM that have template parts. In lit-html's case, it only creates the initial template using .innerHTML, to actually update the DOM it inserts nodes directly into the correct location (and similar for attributes).
For example consider this template:
<template id="exampleTemplate">
<span title="{{foo}}">This is a sample {{bar}} with instantiation</span>
<span>Also {{foo}}</span>
</template>
when we create an instance of the template a set of references are created to the elements that have that name:
{
foo: [
AttributeTemplatePart {
el: /* ref to <span title="{{foo}}"> element */
attrName: 'title',
},
NodeTemplatePart {
previousNode: /* The previous text node before {{foo}}: "Also" */
nextNode: /* The text node after {{foo}} */
},
],
bar: [
NodeTemplatePart {
previousNode: /* The previous text node before {{bar}}: "This is a sample " */
nextNode: /* The next text node after {{bar}}: " with instantiation"
}
],
}
now when we call something like instance.update({ foo: 'banana', bar: 'cabbage' }) all .update needs to do is something like this:
function update(data) {
for (const [key, value] of Object.entries(data)) {
for (const templatePart of this._templateParts[key]) {
templatePart.update(value)
}
this._templateParts[key].update(value)
}
}
this is way more efficient than maintaining a virtual copy of the DOM as we simply implement AttributeTemplatePart and NodeTemplatePart so that they go directly to their location in the DOM and update the value.
e.g. NodeTemplatePart could be implemented like this:
class NodeTemplatePart {
constructor(previousNode, nextNode) {
this._previousNode = previousNode
this._nextNode = nextNode
}
// This is overly simplified and assumes there's a node both before and after
// the {{curlies}}, a real implementation would hold a reference to the parent element
// as well and if there's no previousNode/nextNode it'd just replace the whole contents
update(values) {
if (typeof values === 'string') {
values = [new Text(values)]
}
while (this._previousNode.nextNode !== this._nextNode) {
this._previousNode.nextNode.remove()
}
for (const value of values) {
this._nextNode.parentNode.insertBefore(value, this._nextNode)
}
}
}
Just for documentation and clarification sake, everything @Jamesernator said is the exact same for both _lighterhtml_ and _hyperHTML_, based indeed on the same domdiff library, which uses innerHTML once per unique template tag, on an offline template element, and update the related DOM when needed, it never uses innerHTML again (unless explicitly required by the user, but that's another story)
I think we've steered slightly off-track, so I'll try (hopefully :) ) to get back to what I was intended to say (once again, sorry if I get sidetracked again).
There are two parts to this long-winded comment:
In a tl;dr kind of way my thinking comes to this:
lighterhtml and hyperHTML, based indeed on the same domdiff library, which uses innerHTML once per unique template tag
There are two question arising:
.innerHtml?In my opinion, if we answer and find solutions to these two questions, the entire template instantiation proposal will be rendered moot. To slightly re-word the reasoning behind the proposal:
The HTML5 specification ... doesn't provide a native mechanism to instantiate
with some parts of it substituted, conditionally included, or repeated based on JavaScript values
And then it does correctly say:
making it hard for web developers to combine otherwise reusable components when they use different templating libraries.
The thing is though: is the answer to that a yet another incompatible templating library and syntax? Developers will continue using their own incompatible templating libraries regardless. The reason is simple: any and all templates are limited in functionality and scope. Incompatible templating systems arise because people find that some templating system X is lacking some crucial functionality.
You can see it with Web Components themselves. When they were finalised and started shipping in browsers there was abundant joy. However, just two short years later, the mood has shifted to "web components APIs are a barebones set of low-level primitives aimed at library writers" and people go out of their way to create better more useful abstractions on top. Including pushing everything into strings.
But this was a sidetrack. Let's get back to template instantiation. Among other things mentioned (or shown as use cases):
The reason is obvious: these things are hard to do because DOM APIs are low level, imperative and verbose. However, it has been proven multiple times that these APIs can be made sufficiently high-level, declarative and succinct even in userland. And that they literally solve all of the problems above:
Instead of providing these facilities, the proposal gives us:
if and foreach as the only control structures) and unlimited in functionality (we can call functions in the host language).innerHtml again?)Even though all these problems have been solved dozens of times over by simply providing a better API.
It is my continuing belief that the only right way forward is not to make the platform increasingly weird and complex, but to improve the APIs available to developers and let them figure out what to do with them.
See part 2 on what developers already do with existing APIs and how we can help them by being a better platform.
As I already mentioned a few thousand times :) developers already create DOM trees, diff them, and apply only changes. In userland. Why can't platform itself provide similar APIs and capabilities is beyond my understanding at this point.
However, there are now emerging technologies that may require not just native browser APIs but also a way to provide the browser with a DOM Tree and let the browser figure out what to do. I'm talking about Phoenix LiveView. The idea is as follows:
In the end it does this: 
(For crazy versions, see server-rendered FlappyBird or search Twitter for LiveView).
Template instantiation and updates would have hard time offering anything of value to an approach like this. A friendly native API to tell the browser exactly what you need or a way to declaratively define and provide DOM trees though?
In my opinion template instantiation and the current (and the only, cca 90s) generation of DOM APIs provide none of that and are very reluctant to move forward.
Hi @dmitriid,
Thanks for helping get the conversation back on track (though I did think the previous two comments were quite on-track and informative as well).
In 2019 why is there no better API than dumping blobs of strings into DOM via .innerHtml?
Excellent question! I wish with all my heart that HTML Modules / Imports would have received, in conjunction / parallel with ES Module imports, the same degree of attention the past few years that ES Modules did. But I guess, bowing to the fact that JS imports was in higher demand (an example, in my mind, of questionable coding practices begetting questionable priorities from the standards committees), HTML Modules (and people like me who prefer to send data in their native data format) took a back seat.
But I have good news for you -- with the HTML Modules proposal which is hopefully going to ship soon, it won't be necessary to use .innerHTML to create a template object ready for fast cloning. The server can send a tag with the HTML that needs rapid cloning directly in the document! No API needed at all! But I disagree with you that that answer makes template instantiation less useful -- quite the opposite.
What I'm not yet picking up from you is a recognition of what templates bring to the table -- the ability to quickly clone a chunk of HTML, and add it to the tree using appendChild. I.e the ability to work with repeating data, like grids or virtual lists (or multiple instances of the same web components) efficiently.
I do share the general hope with you that, should template instantiation / and rapid updates of the cloned DOM tree ship in the browser, that whatever low-level functions are needed to do that in as fast a manner as possible -- a DOM Diff engine of sorts, in other words -- that where possible, (some of) those low-level functions might also be added to the platform, which could benefit other scenarios, such as scenarios where the original HTML didn't come from a template, but may have instead come from server-side rendered HTML. I.e., tell the engine via an api where to locate the "parts" for rapid updates. Scenarios where the HTML isn't very repetitive, which doesn't exhibit the kind of repetitive structures templates are designed to optimize. Or scenarios where a client-side API is used to generate the entire HTML structure without the help of templates (hard as it is for me to imagine why that would be the right approach).
@dmitriid: Thanks for a very insightful feedback. I love when people give very constructive & detailed feedback based on use cases & case studies like that.
As I stated somewhere before, our approach to template instantiation broadly has three goals:
I understand that the need for (1) is quite high, and your criticism of the original proposal is understandable because it had many flaws. However, that is the point of making a proposal, and we're happy to work with you and the rest of web developer community to figure out the right set of primitives and API surface to add to the platform.
Template instantiation and updates would have hard time offering anything of value to an approach like this. A friendly native API to tell the browser exactly what you need or a way to declaratively define and provide DOM trees though?
That's more or less NodeTemplatePart and AttributeTemplatePart aim to provide. When you set a new value to a template part, the browser can figure out whether the change actually needs to propagate to the node or not by figuring out the value change. We didn't include any node diff'ing algorithm into the proposal but it could be done.
We've been working with @justinfagnani from Google to improve our proposal ever since we made the initial proposal, but it would be really helpful to document what you consider to be the ideal API. For example, @WebReflection's proposal about DocumentFragment in https://github.com/whatwg/dom/issues/736 has been very useful for us because that's more or less where I started before I eventually warmed up to the idea of NodeTemplatePart.
I read all of this, and although I'm very excited about Template Instantiation, I agree with @dmitriid that it's quite high level, bring mustache syntax, often use as a top abstraction template engine, as the only solution for truly dynamic HTML.
If there was something like a native patch api, this wouldn't have been required, as the template could have been generated by any abstraction.
I'd like to emphasise the need for this sort of API cause since 10 years, developers have come up with different solutions and abstraction for this very problem...
DOMChangeList API seems to offer a similar approach to what Template Instance does, in the sense that once you figure out changes, you can delegate that to a low-level DOM API that already applies them for you.
My personal experience working with JSX at least, us that it's very nice to be able to use JS in a declarative way to build your template. You don't need to learn any new DL and it just works, letting you write template logic as you please.
The problem I see with a DOM diffing low-level API is only one.
As mentioned above, you don't always need DOM diffing. You could create a DOMChangeList based on the amount of changes coming from state changes. For specific cases, that would be more efficient.
So, I do agree that we require a way for the underlying API to know where changes will happen in the document. In that way, the low-level API can choose which technique is best to apply, based on his knowledge of the template and where changes are.
Ultimately, I disagree in general with statements like 'Template Instantiation does not bring anything to the table, it does add much', just because of what I wrote above...
But I don't like for the native standard to choose the template syntax you should use.
If there was a way to change the proposal to support JS instead of mustache, I don't know maybe a template strings like solution...
I realised I've been absent from this discussion for quite some time. But, you know, there's always someone wrong on the internet, so my attention got diverted :)
I realised the main reason we don't agree on what template instantiation. Template instantiation presumes that the template already exists. That is, it's a static chunk of HTML (as a string?) with some dynamic parts described using the moustache syntax. And then that static chunk gets applied to the document.
See for yourself (I'll use the quote format to present all my arguments as a single chunk):
with the HTML Modules proposal which is hopefully going to ship soon, it won't be necessary to use .innerHTML to create a template object ready for fast cloning.
HTML Modules assumes there's already some static HTML defined and ready for import
what templates bring to the table -- the ability to quickly clone a chunk of HTML, and add it to the tree using appendChild.
This assumes that the HTML chunk already exists
That's more or less NodeTemplatePart and AttributeTemplatePart aim to provide. When you set a new value to a template part, the browser can figure out whether the change actually needs to propagate to the node
This also assumes that those parts are already there, and inserted into the document
However, the only reasonable API for creating those templates? Probably .innerHTML :)
So, in my mind, the template instantiation proposal for some reason separates creating and updating dynamic HTML into several stages:
.update, and the values will be updated (the proposal)However, from the point of view of most developers (yup, speaking for everyone here :) ) there's only one stage: given a certain state, update the DOM.
A common example: Load a list of items. While they are being loaded, display "Loading". Once they are loaded, display them.
In most templating systems, or in any JS-only approach like React's JSX, it's the same thing:
// using JSX
return <div>
{{ loading ? 'Loading...' : items.map(i => `<div>${i.name}</div>`}) }}
</div>
Template Instantiation as it currently stands has no support for this use case:
.update or .appendChild?)And that's just for the most simple use case.
I also doubt these new APIs will gain any significant adoption as "a common set of primitives for various libs and frameworks to work together". To begin with, there already is a common set of primitives, the DOM API ;) But the main reason is that I doubt template APIs provide any significant improvement over what current generation of libraries and frameworks have to deal with:
Now | With Template APIs |
--- | ---
Only some can be "rehydrated" from static HTML | More or less assumes the template is already present as static HTML
Can create any complex structure on the fly | Unless already defined as static templates, you will have to do what libraries and frameworks already do
Manually create DOM nodes and append/insert them into the document for dynamic parts such as lists/list items | Manually create DOM nodes and append/insert them into the template (unless it's already a template, then manually instantiate and append/insert)
Manually keep track of changes to change the DOM only as needed | Same (the proposal doesn't specify how the browser should work with data changes)
Manually track changes in lists of items (commonly using key attribute) to make sure lists are not re-rendered in their entirety | Possibly same, possibly not. No idea how the browser will re-render items in a loop
Use any and all tools at your disposal: create any template/DSL you want, or use JS. | Restricted to a very limited subset of moustache syntax with callbacks into JS. Conditionals left to libraries and frameworks, loops are not specified yet.
In my opinion, this is a marginal improvement at best, and the cost is significant. I've already talked abut it here: https://github.com/w3c/webcomponents/issues/704#issuecomment-478696830
Thanks @dmitriid for taking the time to patiently explain why I am wrong :-)
To be honest, I've come to realize that there seems to be an "East is East and West is West and never the twain shall meet." I think we are at opposite poles in terms of what appeals to us, and the point of discussing further seems to be pointless, though it makes for good comedy. Maybe we should start an act? :-)
For example:
Template instantiation presumes that the template already exists.
This is our fundamental disagreement? Is this a "chicken or the egg" type puzzle?
It exists.. And is widely used..
Why not make it even more useful?
somehow create the template (how?) ...
However, the only reasonable API for creating those templates? Probably .innerHTML :)
In the context of an HTML Module, the mystery eludes me. Why use an api? Why not type the text of the template into a static file? That doesn't use innerHTML. Or copy and paste from the first link above into a static file? I don't see an innerHTML button on my mouse. If the template to be cloned contains some pieces of semi-live data (but which will be repeated in every instance on the client) why not use PHP? Asp.net? The sky is the limit. Few if any of them use innerHTML to my knowledge. Surely, you know this, so I start to think you are speaking in some deeper level way beyond my comprehension. Are you using "innerHTML" as an allegory for something deeper? :-)
I'm afraid I don't have a good ear for poetry :-)
I get that I'm the freak here who likes the syntax of Vue and Svelte more than JSX. (Actually, I'm pleasantly surprised you appear to be warming up to template literals in your JSX example?) I work with JSX on a daily basis, and isn't my cup of tea. Sorry. That's just me. There's no accounting for taste.
And we still have plenty of performance problems, which has certainly not increased my affinity for it.
And by the way, I do share the concerns raised by Jan and Mildred above that the ability to apply transforms beyond what inline binding can provide is quite important.
If I may be so bold, could I suggest that what is really bothering you is that the proposal appears to favor the syntax of Vue and Svelte over your beloved React? I think that is a valid concern, but that may just be a kind of "illusion". In fact, I suspect it's that very concern that causes the proposal to be so vague and leave so much up to the frameworks, which you are using against it. I see that as a feature, not a bug.
I look for common goals when I communicate with others-- surely performance is a common goal? But you don't seem to want to engage in the question I find most interesting -- whether it's faster to generate a list by:
vs.
...
Whatever it is that your compiler does with the JSX / template literal you provided?
And that's the thing. The compiler would presumably do whatever is fastest. That could, today, already be using templates without the instantiation proposal. Or maybe not. Maybe it's lots of appendChilds, which I think React uses. Maybe its lots of innerHTML's, which I think React used to use, but concluded lots of appendChilds is faster (I remember reading that a long time ago, but can't find it now). Interestingly, the folks at ag-grid concluded the opposite (see Hack 5). So I don't know what to think.
I'll give you the last word as we are going in circles
@bahrus I engage in this conversation in good faith. You engage in thinly veiled trolling.
For the sake of others in this thread, I'll respond to some of the things you mention:
In the context of an HTML Module, the mystery eludes me. Why use an api? Why not type the text of the template into a static file?
Because not everything is a static HTML file. For a person who "deals with JSX daily" this should be obvious.
If the template to be cloned contains some pieces of semi-live data (but which will be repeated in every instance on the client) why not use PHP?
Because not everything is a static HTML file. Because not everything is HTML generated on the server and sent to the browser.
I get that I'm the freak here who likes the syntax of Vue and Svelte more than JSX.
If I may be so bold, could I suggest that what is really bothering you is that the proposal appears to favor the syntax of Vue and Svelte over your beloved React?
One more reason to think that from the offset you weren't interested in an honest discussion.
I suspect it's that very concern that causes the proposal to be so vague and leave so much up to the frameworks
The proposal is very concrete in all the places that sort of really matter (how to get data into the templates), and is vague in all the places where it matters (conditionals and loops).
So, no, I'm not thrilled by things being left to library and framework authors _yet again_ when we could have high performing APIs built into the browsers, and not reimplemented time and again in incompatible ways in hundreds of different frameworks.
But you don't seem to want to engage in the question I find most interesting -- whether it's faster to generate a list by
Your bullet points are invalid in the context of the proposal. Moreover, I gave a concrete, simple example, which would be rather painful to implement within the confines of the proposal, especially when HTML is generated dynamically.
I also showed all the ways the proposal is not strictly or much better than existing approaches for all the reasons that the proposal omits, and for all the reasons that this entire discussion deliberately omits.
If you think this proposal obviates the need for existing frameworks, or that React/Vue/Preact/morphdom/hyperHTML/.update call happens.
So I don't know what to think.
Indeed.
Apologies, @dmitriid, for my overly defensive assumption about your preference for JSX over Vue / Svelte style syntax. I'm sure it was me reading too much, I guess, into your use of the word "moustache" in what I thought was a critical paragraph, combined with the previous comment, who said (I think) that he agrees with you, that he too finds the moustache syntax problematic in the same sentence, and which you didn't refute.
I'm sure I wasn't very clear, but what I was trying to say was the opposite -- that the proposal is designed to work with frameworks (I think) or additional helper libraries, not to obviate them.
I will work on my communication skills in the future to figure out how I can engage in these discussions, and not come off as trolling, as I seem to be misunderstanding everyone.
Apologies again.
@dmitriid While the precise syntax and semantics of what builtin templates will be included are not worked out yet ((see this issue)). But it'll almost certainly look something like this:
<div>
<template directive="if" value="loading">
Loading...
</template>
<template directive="else">
<template directive="for-each" in="items">
<div>{{i}}</div>
</template>
</template>
</div>
Regardless of what the builtin semantics are I don't see how it really matters, the point of the default template instantiation processor is not to provide a turing complete language capable of doing any computation you could imagine but rather provide a good subset for a good chunk of typical use cases.
There's always gonna be use-cases that template instantiation is not going to solve by itself, for those cases there exists the DOM to do whatever manipulation on whatever data-structures you might need and quite importantly TemplateParts with which to have your own insertion points for data from those structures.
It's also not really clear to me what you're arguing, you keep referencing your original post as if virtual DOM is some holy grail of performance and usability which is a highly subjective point at best. I personally prefer the declarative HTML form as it's a lot easier in a large amount of structural elements to separate your presentation from any data you carry.
Regarding your points:
somehow create the template (how?)
somehow insert it into the document or into another template (how?)
when values change, call .update, and the values will be updated (the proposal)
Yes, I'm not clear what is wrong with this? There's already perfectly canonical ways of inserting it into the Document/ShadowRoot e.g. .appendChild. The typical flow would look like this:
import { loadingTemplate } from "./loadingTemplate.html"const instance = loadingTemplate.createInstance()shadowRoot.appendChild(instance)instance.update({ changedKey: newValue })you can very easily wrap this flow into a PureComponent helper or something in a lot of cases. For other cases you can always use DOM as if you don't know ahead of time what structure you might be constructing you probably need some imperative logic anyway.
Because not everything is a static HTML file. For a person who "deals with JSX daily" this should be obvious.
Because not everything is a static HTML file. Because not everything is HTML generated on the server and sent to the browser.
Heaps of stuff is just static content that you want to include in the page for its structure rather than anything dynamic. The whole point of template instantiation is to _also_ provide you insertion points where you want dynamic data to go. And as I've already said, even when things are dynamic you almost always have a certain number of insertion points.
One final thing, the spec text in this repository is not necessarily up to date or what will be implemented in browsers. There's currently a WIP implementation in Chrome to implement template instantiation which simply by looking at the WebIDL definitions I can see has a fair few differences from the spec in this repository.
I would like to remind you all of the original question raised:
Before adding anything as complicated as https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Template-Instantiation.md it is better to ensure it improves the platform.
Once again, this proposal introduces an objectively complex:
And introduces only marginal improvements (if they are improvements at all). And on top of all that it basically leaves nearly everything to the libraries and frameworks.
Yesterday I came up with the simple "Loading..."/list-of-DOM-elements example. The solution suggested by @Jamesernator already shows how horribly cumbersome the alternative provided by proposal is.
Then I realised there's an another example:
<button onclick="{{ f }}"></button>
The proposal provides no way of dealing with this except manually parsing this, figuring out what the attribute is, setting up the listeners etc. That is, once again, delegating everything to the libraries and frameworks. And these are not corner cases, or edge cases. These are actual use cases that people come up against every single day.
So, instead of giving the power to end devs, once again, the standards opt for "we're going to introduce a very limited, underspecified, cumbersome API that is only aimed at developers of libraries and frameworks". Can we stop this trend, please? :)
@Jamesernator
It's also not really clear to me what you're arguing, you keep referencing your original post as if virtual DOM is some holy grail of performance and usability which is a highly subjective point at best.
Would you please give me at least a sliver of respect and read what I am actually writing. For example, here: https://github.com/w3c/webcomponents/issues/704#issuecomment-478696830 where I actually talk about _killing the need for VDOM_
Ah. I clean forgot one more thing. This proposal lacks a simple cost-benefit analysis.
The proposal lists 9 use cases and provides satisfactory solutions to at most one or two if them.
And despite this fact, and despite the fact there’s an ongoing debate around implementation of conditionals and loops, etc. etc. of course Chrome is already implementing it.
Because who wouldn’t want another half-baked underspecified vague standard in the browser around which everyone will be busy building new workarounds and abstractions (because its nigh unusable otherwise).
Would you please give me at least a sliver of respect and read what I am actually writing. For example, here: #704 (comment) where I actually talk about killing the need for VDOM
I have read it and your proposed solution is essentially still just a VDOM/tree-diff except generating a patch on the server-side instead of the client. However this forces the server to handle all work which may or may not be desirable depending on your application.
It's definitely not bad but it definitely has some major drawbacks:
And also note this approach still can suffer the usual tree-diff issues:
<video>), in contrast with templates you have the decision as to whether or not to re-use the same tree again (or any strategy you want)already shows how horribly cumbersome the alternative provided by proposal is.
"horribly cumbersome" is highly subjective, regardless you could propose your own alternative syntax if you feel strongly about it and believe that more concise syntax would significantly improve adoption.
I would personally prefer something like <for.each in="loading"><!-- contents --></for.each> but it's not clear what impact this would have on existing HTML parsers that expect only <template> to have parsed but inert content. I think I'll raise the matter on the issue about the default processor because I'm not super clear if it would actually be a problem.
Then I realised there's an another example:
Again there's an open issue which is specifically about the capabilities of the default processor. I do suggest proposing any things you think will be really common use cases there.
The proposal provides no way of dealing with this except manually parsing this
Even in underspecified current spec there's absolutely nothing that requires you to parse it yourself. Just delegate to the default processor to handle parsing and replace any AttributeTemplatePart with your own parts.
we're going to introduce a very limited, underspecified
I highly doubt that proper spec text won't be released before the feature is shipped.
Regarding your original points:
a new templating language (incompatible in syntax and behaviour with everything else in the ecosystem)
I don't know what you mean by "incompatible in syntax and behaviour", there's a wide difference in syntax and behaviour (for example Vue uses v-bind:attr="someProp" whereas JSX uses attr={someExpression}). What do you consider to be "compatible in syntax and behaviour"?
The proposal has to choose some syntax it can't possibly choose all of them, I don't see minor syntactic differences being the deal-breaker of the proposal.
that is both very limited in functionality (poorly specified/underspecified if and foreach as the only control structures) and unlimited in functionality (we can call functions in the host language)
This doesn't seem to be so much an issue with template instantiation itself as a lack of concrete spec text.
that is alien to the system (literally nothing in the platform looks or behaves like this)
True, but that's true of any new feature.
that requires a different parser
Nope it doesn't, I've implemented an almost full polyfill of it and the way it works in both the current spec and my polyfill is it simply looks at attribute and text nodes for {{. For example consider <p attr="{{value}}">, we don't need a new parser to handle this at all, we just check if the attr contains {{ and if it does we replace it with a _Template Part_.
a different execution model
I don't understand this you'll need to elaborate. But it seems to me that anything would be a "different execution model".
hooks into the rest of the platform to make it workable
Again I don't understand this. It seems to me that anything would have "hooks into the rest of the platform", that's kinda the point of _Template Parts_ is that they hook into the DOM and can dynamically update minimal parts of the DOM.
solves the main use case of composing components poorly, or not at all (you need to nest template elements within template elements within template elements...)
This seems to be quite syntax opinionated but you haven't actually proposed a syntax alternative that you'd prefer.
doesn't specify how you would instantiate elements not from static sources, but from dynamic data (I'm guessing .innerHtml again?)
You shouldn't need .innerHTML for anything. Either use DOM for the cases when you actually truly arbitrary structures, but templates should suffice for anything with any degree of structure (which is most things).
@Jamesernator
I have read it and your proposed solution is essentially still just a VDOM/tree-diff except generating a patch on the server-side instead of the client.
There’s more there than just server-side rendering, but I’ve removed the rest if my comment, as it was written in a provocative tone. I’ll come back with a proper detailed answer that also keeps the original question in mind.
I apologize to thise who had to read my original reply.
This has been quiet for nearly a year... is template instantiation still a "thing"? What's the current status of it? (As in, what should happen at this stage to see it progress?) I can't find references to it anywhere other than the proposal itself and a few articles from 2017 pointing to it.
This has been quiet for nearly a year... is template instantiation still a "thing"? What's the current status of it? (As in, what should happen at this stage to see it progress?) I can't find references to it anywhere other than the proposal itself and a few articles from 2017 pointing to it.
Yes, we're still interested in pursuing this proposal & solutions in this space. However, we're currently focused on resolving gaps in the existing Shadow DOM & Custom Elements APIs like improving the focus & accessibility support.
I've worked with @hober @justinfagnani @yuzhe-han @mfreed7 to make a refined API proposal for templates:
https://github.com/rniwa/webcomponents/blob/add-dom-parts-proposal/proposals/DOM-Parts.md
There is a PR to merge it into this repository.
We'd discuss it at tomorrow's TPAC breakout session.
Most helpful comment
Yes, we're still interested in pursuing this proposal & solutions in this space. However, we're currently focused on resolving gaps in the existing Shadow DOM & Custom Elements APIs like improving the focus & accessibility support.