Re: [webcomponents] Imperative API for Insertion Points

On Sun, Feb 16, 2014 at 12:52 AM, Ryosuke Niwa <rniwa@apple.com> wrote:

> On Feb 16, 2014, at 12:42 AM, Ryosuke Niwa <rniwa@apple.com> wrote:
>
> On Feb 15, 2014, at 11:30 PM, Alex Russell <slightlyoff@google.com> wrote:
>
> On Sat, Feb 15, 2014 at 4:57 PM, Ryosuke Niwa <rniwa@apple.com> wrote:
>
>> Hi all,
>>
>> I’d like to propose one solution for
>>
>> [Shadow]: Specify imperative API for node distribution
>> https://www.w3.org/Bugs/Public/show_bug.cgi?id=18429
>>
>> because select content attribute doesn’t satisfy the needs of
>> framework/library authors to support conditionals in their templates,
>> and doesn’t satisfy my random image element use case below.
>>
>>
>> *== Use Case ==*
>> Random image element is a custom element that shows one of child img
>> elements chosen uniformally random.
>>
>> e.g. the markup of a document that uses random-image-element may look
>> like this:
>> <random-image-element>
>>   <img src="kitten.jpg">
>>   <img src="cat.jpg">
>>   <img src="webkitten.jpg">
>> </random-image-element>
>>
>> random-image-element displays one out of the three img child elements
>> when a user clicks on it.
>>
>> As an author of this element, I could modify the DOM and add style
>> content attribute directly on those elements
>> but I would rather use shadow DOM to encapsulate the implementation.
>>
>>
>> *== API Proposal ==*
>>
>> Add two methods void add(Element) and void remove(Element) to content
>> element.
>> (We can give them more descriptive names. I matched select element for
>> now).
>>
>> Each content element has an ordered list of **explicitly inserted nodes*
>> *.
>>
>> add(Element element) must act according to the following algorithm:
>>
>>    1. If the content element's shadow host's node tree doesn't contain _
>>    *element*_, throw HierarchyRequestError.
>>    2. If element is already in some other content element's _*explicitly
>>    inserted nodes*_
>>    then call remove with _*element*_ on that content element.
>>    3. Append _*element*_ to the end of _*explicitly inserted nodes*_.
>>
>>
>> remove(Element element) must act according to the following algorithm:
>>
>>    1. If the content element's _*explicitly inserted nodes*_ does not
>>    contain _*element*_, throw NotFoundError.
>>
>>
> Throwing exceptions is hostile to usability.
>
>
> If people are so inclined, we don’t have to throw an exception and
> silently fail.
>
>
>>    1. Remove _*element*_ from _*explicitly inserted nodes*_.
>>
>>
>> The idea here is that _*explicitly inserted nodes*_ of an insertion
>> point A would be the list of distributed nodes of A but
>> I haven't figured out exactly how _*explicitly inserted nodes*_ should
>> interact with select content attribute.
>>
>> I think the simplest model would be _*explicitly inserted nodes*_ simply
>> overriding whatever select content attribute was
>> trying to do but I don't have a strong opinion about how they should
>> interact yet.
>>
>> I don't think it makes sense to support redistributions, etc... at least
>> in the initial API.
>>
>>
>> This proposal has an advantage over the existing proposal on
>> https://www.w3.org/Bugs/Public/show_bug.cgi?id=18429:
>>
>>    1. It doesn't require UA calling back to JS constantly to match
>>    elements
>>    2. Point 1 implies we don't expose when distribution happens for
>>    select content attribute.
>>
>> This doesn't seem like progress. I'd hope an imperative API would,
> instead, be used to explain how the existing system works and then propose
> layering that both accommodates the existing system and opens new areas for
> programmatic use.
>
> We can imagine such a system for programmatic Shadow DOM with some sort of
> distribute(Element) callback that can be over-ridden and use add/remove
> methods to do final distribution.
>
>
> The problem here is that such a callback must be called on every node upon
> any state change because UAs have no way of knowing what causes
> redistribution for a given component.  As as a matter of fact, some use
> cases may involve changing the node distributions based on some JS objects
> state.  And having authors codify such conditions for UAs is much more
> cumbersome than letting them re-distribute nodes at their will.
>
>
> To give you more concrete example, in the case of my random image element,
> how can UA notice that user clicking on the element should trigger
> reconstruction of the composed tree?
>

Isn't the stated design of the custom element that it re-constructs the
composed tree with a random image every time it's clicked? It's not
actually clear what you wanted here because there isn't any example code to
go on.


>  Should the script call some method like redistribute() on the host upon
> click?  But then, since the element needs to pick a child uniformly random,
> it probably needs to keep track of the number of children to be distributed
> and return true exactly when that node was passed into the callback.
>  That’s an extremely cumbersome API at least for my use case.
>

I have the sense that if you produced example code you'd be able to make a
better guess about what's onerous and what isn't. As it is, we're debating
hypotheticals.

Here's a version of your component based on my proposal. I don't feel it's
particularly cumbersome in context:

    var log = console.log.bind(console);
    var randomInt = function(min, max) {
      return Math.floor(Math.random() * (max - min + 1)) + min;
    }

    var rip = Object.create(HTMLElement.prototype);
    rip.distributeCallback = function(children) {
      while(this.shadowRoot.children.length) {
        this.shadowRoot.remove(this.shadowRoot.children[0]);
      }
      var i = randomInt(0, children.length-1);
      this.shadowRoot.add(children[i]);
    };
    var RandomImage = document.registerElement(
                          "random-image", { prototype: rip });


Obviously, the element needs to add a click handler to call
distributeCallback() manually with this.children, but I don't see how
that's hard.

What did I miss?

Received on Sunday, 16 February 2014 09:22:10 UTC