Re: [Shadow DOM] Separating Transclusion Mechanisms for Inheritance and Data Binding

On Apr 21, 2014, at 4:36 PM, William Chen <wchen@mozilla.com> wrote:

> On 4/17/14, 2:42 AM, Ryosuke Niwa wrote:
>> Review: Template Inheritance in the Current Specification
>> 
>> In the current specification, a super class doesn't define any hooks for subclasses.  Instead, it defines insertion points into which nodes from the original DOM ("light DOM") is inserted, and then subclasses use shadow element to replace elements that get distributed into superclass's insertion points.
> I'm going to assume that you were writing this email against the version of the spec that had <shadow> as a function call semantics since that's what it looks like from your examples, so I will respond as if that feature is still there to address some of your points.

Right.

> Content insertion points in the super class select nodes from the subclasses shadow element, except when there is no subclass, in which case it selects from the light DOM. Thus you can use insertion points to act like an inheritance hook for subclasses. It is up to the subclass to decide if it wants to propagate any nodes from the light DOM into the super class using its own insertion points within the <shadow> element.

This is precisely what we don't like about the old specification that used the shadow element as a function call semantics.  It confuses data binding against the "light" DOM and subclass inheritance hook.  Furthermore, it ought be superclass that opt-in to let subclass translate its content; not subclass that transcludes arbitrary transclusion points its superclass defined for data binding.

>> Alternative Approach to Inheritance
>> 
>> Consider random-element which picks a random child node to show whenever a user clicks on the element.  This element may show the name of probability distribution it uses to pick a child in its shadow DOM.  The name of the probability distribution is in the definitions of subclasses of random-element, and not in the light DOM of this custom element.  If we wanted to use the current inheritance model (multiple generations of shadow DOM & shadow element), we have to either replace the entire shadow DOM in the subclass to show the name of the probability distribution that subclasses use or add an attribute, etc… to identify the element that contains the name of probability distribution inside subclasses' shadow element. The latter would be weird because there is nothing preventing from the user of random-element to put an element that matches the same selector as a child of random-element in the "light DOM".
> The super class could have an insertion point for the distribution name selecting on a class, etc. The way distribution worked was that the older shadow root would only distribute child nodes from the younger shadow root's shadow element, so the subclass has a great degree of control over distributed nodes. The only way to leak the user's nodes from the "light DOM" would be if the subclass pulled nodes from the user's "light DOM" with content insertion points in the shadow element. Simplified example:
> 
> older shadow root (superclass):
> <content select=".name"></content>
> <content select=".pickme"></content>
> 
> younger shadow root (subclass):
> <shadow>
>     <span class="name">Non-random Distribution</span>
>     <content select=".pickme"></content>
> </shadow>
> 
> light dom:
> <random-element>
>     <span class="name">This will not be distributed to the older shadow.</span>
>     <span class="pickme">This will be distributed to the older shadow.</span>
> </random-element>
> 
> Again, this is only the case if the <shadow> as function call gets added back to the spec.
>> For implementors, this new model doesn't require multiple generations of shadow DOM for a single host.  Each element can have at most one shadow root, and its shadow DOM simply contain yield element that defines transclusion point.  Furthermore, transclusion is simply done as attaching a new shadow DOM to yield element.  If we wanted the same expressive power as the current inheritance model in grabbing light DOM's nodes, we can make insertion points pull nodes out of the parent shadow DOM's light DOM instead.
>> 
>> For authors, the new model separates the concerns of binding DOM data model to shadow DOM from defining inheritance hooks.  It addresses use cases where inheritance hooks for subclasses are separate from data source used by custom elements such as random-element showing the name of distribution, which is overridden by its subclasses.
> It seems like we can use insertion points as both inheritance hooks and data binding right now to solve the same use cases as yield. I'm not too sure I understand what we gain from yield that we can't do otherwise.

This wouldn't work if superclass weren't abstract.  If an author instantiated the superclass itself, there is nothing preventing the light DOM to have an element that matches ".name".  It's possible to work around this limitation by treating the superclass as an abstract class and defining a subclass that works around it but that strikes me as a terrible developer ergonomics story since we'd have to do that for every single class that defines an inheritance-only transclusion points.

"yield" element naturally separates inheritance hook (automatic creation of nested shadow root) from dating binding (insertion points), and can be _explained_ in terms of nested shadow DOMs, which already exists in the case of nested components.  It's much simpler to understand and reason about for authors.

If one of goals of web components is to introduce new primitives that could explain and extend the Web platform coherently, we shouldn't be introducing TWO new primitives (insertion points and multiple generations of shadow DOM) that aren't even used by UAs to implement builtin elements.

> I do like the idea about separating data binding and inheritance hooks, but I'm not sure yield is the way to do it. We sometimes treat DOM nodes as data, and other times we don't, in the end we just transclude them in a very similar way so any distinction between the two would be mostly syntactic.

Given that many existing JS frameworks already implements some kind of named "yielding" mechanism similar to the one we proposed in this thread, I have a hard time believing that treating shadow element as a function call semantics works better.

> There would be little in the way of people abusing data bindings as insertion hooks or insertion hooks as data bindings. I think if we wanted to make the distinction, we would need a data insertion point that transcludes something that looks more like data than an arbitrary DOM node.

You mean like binding to a JS object?  Indeed, one of the biggest concerns we (Apple) have is the lack of mechanism to update DOM nodes from JS objects since just about every popular JS framework such as Amber.js and AngualrJS do that.  We (Apple) would love to discuss API design for that.

- R. Niwa

Received on Tuesday, 22 April 2014 04:43:39 UTC