Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Composing v1 nested web components

I'm new to webcomponents. Since v1 of webcomponents is available, I'm starting there. I have read various posts around the web about them. I'm particularly interested in composing them correctly. I have read about slots and have them working, although my efforts have not resulted in slotted webcomponents that work the way I expected.

If I have compose nested web components like this, the DOM from the nested/slotted webcomponent does not get inserted in the slot of the parent:

<parent-component>
  <child-component slot="child"></child-component>
</parent-component>

And here's the parent webcomponent HTML:

<div id="civ">
  <style>
  </style>
  <slot id="slot1" name="child"></slot>
</div>

Since each webcomponent (parent and child) is written to be independent, I have been creating them with:

customElements.define('component-name', class extends HTMLElement {
  constructor() {
    super();
    this.shadowRoot = this.attachShadow({mode: 'open'});
    this.shadowRoot.innerHTML = `HTML markup`
  }
})

The resulting DOM includes 2 shadow roots. I have attempted to write child/slotted elements without shadow dom, which also does not result in the parent shadow dom hosting the children.

So, what is the right way to compose v1 nested webcomponents?

like image 947
rob_hicks Avatar asked Aug 16 '16 13:08

rob_hicks


Video Answer


1 Answers

First, you must use a browser that implements Shadow DOM and Custom Elements v1.

Then the call to attachShadow() will assign the new Shadow DOM automatically to the read-only property shadowRoot.

You can append the HTML code to the Shadow DOM's innerHTML but I'd recommend to use a <template>'s content property instead.

Then nesting is natural:

customElements.define( 'parent-component', class extends HTMLElement {
    constructor() {
        super()
        this.attachShadow( {mode: 'open'} )
        this.shadowRoot.appendChild( parent1.content.cloneNode( true ) )
    }
} )
            
customElements.define( 'child-component', class extends HTMLElement {
    constructor() {
        super()
        var sh = this.attachShadow( {mode: 'open'} )
        sh.appendChild( child1.content.cloneNode( true ) )
    }
} )
<parent-component>
    <child-component slot="child">
        <span>Hello</span>
    </child-component>
</parent-component>


<template id="parent1">
    <style>
        :host { background: lightblue ; display: inline-block }
        ::slotted( [slot=child] ) { background: lightyellow }
    </style>
    <h4>(parent)</h4>
    <p>Slotted child:
        <slot name="child"></slot>
    </p>
</template>    

<template id="child1">
    <style>
        :host { display: inline-block }
        ::slotted( span ) { background: pink }
    </style>
    <h5>(child)</h5>
    <span>Nested slot: <slot></slot></span>
</template>

In the <style> tags, use:

  • :host to style the custom element itself, and
  • ::slotted() to style the elements inserted with a <slot> tag.
like image 86
Supersharp Avatar answered Sep 20 '22 21:09

Supersharp