WICG / webcomponents

Web Components specifications
Other
4.38k stars 373 forks source link

[declarative-custom-elements] how does the class get associated with the definition #884

Open trusktr opened 4 years ago

trusktr commented 4 years ago

In the following,

<definition name="my-element">
    <template>....</template>
    <script type="module">
        class MyElement extends HTMLElement {
            #template;
            constructor(state, ...args) {
                super(state, ...args);
                #template = customElements.attachTemplateAsShadow(this, state);
            }
            render(state) {
                #template.update(state);
            }
        }
    </script>
</definition>

how does the engine know to define the <my-element> element using the MyElement class?

How does type="module" come into play here? What happens if type=module were not included?

What happens if I write two classes in the script tag? F.e.

<definition name="my-element">
    <template>....</template>
    <script type="module">
        class MyElement extends HTMLElement {
            // ...
        }
        class MyOtherElement extends HTMLElement {
            // ...
        }
    </script>
</definition>

Or is the example in the proposal missing an export? Should it be

<definition name="my-element">
    <template>....</template>
    <script type="module">
        export default class MyElement extends HTMLElement {
            // ...
        }
    </script>
</definition>

?

Happy to propose changes to the proposal file once this is cleared up.

trusktr commented 4 years ago

I'll assume that the proposal was missing the export part.

What happens if there are multiple script tags?

<definition name="my-element">
    <template>....</template>
    <script type="module">
       export default class MyElement extends HTMLElement {
           // ...
       }
    </script>
    <script type="module">
       export default class MyOtherElement extends HTMLElement {
           // ...
       }
    </script>
</definition>

Should the <definition> accept and execute only the first script tag? Or maybe execute any script tags like normal, but take the export only from the first script tag?

Maybe the following is for another spec, but it seems there should be a way, in general, to grab the exports of a type=module script tag, f.e.

<script type="module">
   export default class MyOtherElement extends HTMLElement {
       // ...
   }
</script>
<script>
  const s = document.querySelector('[type=module]')

  // Similar to ES import():
  s.import().then(m => console.log(m.default)) // logs MyOtherElement class
</script>

Or maybe also something like

<script type="module" name="MyOtherElement">
   export default class MyOtherElement extends HTMLElement {
       // ...
   }
</script>
<script>
  const s = document.querySelector('[type=module]')

  // Using ES import():
  import('script:MyOtherElement').then(m => console.log(m.default)) // logs MyOtherElement class
</script>
justinfagnani commented 4 years ago

The class should indeed be export default.

What happens if there are multiple script tags?

I think we can easily say that only the first of last module is used.

bahrus commented 1 year ago

I just wanted to throw out the possibility that we could morph server rendered HTML that is rendered into the live DOM tree into a "template" for a web component that could be used by other single tags. An example of how this could be useful: A server-rendered side nav component used for the hamburger menu. Then it could "progressively" be used to define the web component. Another example would be a slide show web component, where we want to display the initial slide before all the dependencies have been loaded.

POC here.

I think the performance would be better, especially if the JavaScript may have numerous dependencies that need to load before the web component could become active.

bahrus commented 1 year ago

It would also allow streaming to be utilized for the first instance of the declarative web component.

bahrus commented 1 year ago

Something to note: This solution does not require that the developer write any JavaScript, not even defining a class! Just specify prop names, which can be set via attributes, which template instantiation (especially template instantiation that uses comments for placeholders ) could fill in. Basically, do as much as possible with JSON definitions (which I believe could cover a large chunk of declarative web components).

sashafirsov commented 1 year ago

DCE does not have to use JS and IMO the "pure" DCE would be JS-less. The class would be created by the browser and use the native templating for content rendering. The element life cycle events and callbacks can be assigned either as inline onxxx or associated by embedded SCRIPT sections, something like

<script type="element/connectedCallback" >... with default export of callback

or

<script type="module" >document.currentScript.getRootNode().connectedCalback = function(){....} But it would require to add currentScript to ES6 module JS API. As of now it is only in ES5 and there is a struggle to get it approved even for inline scripts.

The <script type="module"> is optional in pure declarative case and would be needed only when there is a need to redefine the constructor. Which I hope would be less demanded when pure declarative syntax is fully functional.

sashafirsov commented 1 year ago

@trusktr , Should customElements.attachTemplateAsShadow(this, state) be separate proposal ? I see the need for such API, but not on customElements . Rather follow the pattern of element.attachShadow

keithamus commented 1 year ago

WCCG had their spring F2F in which this was discussed. You can read the full notes of the discussion (WCCG had their spring F2F) in which this was discussed, heading entitled "Declarative Templating & Custom Elements".

This was briefly discussed as part of that topic. The consensus from present members of the WCCG was that more use cases need to be captured around this area, in order to clarify what these proposals solve.