Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web components: How to work with children?

I'm currently experimenting with StencilJS to create some web components.

Now I know that there is <slot /> and named slots and all that stuff. Coming from React, I guess slot is similar to children in React. You can do a lot of stuff using children in React. Things I often did:

  1. Check if any children are provided
  2. Iterate over children to do something to each child (e.g. wrap it in a div with a class etc.)

How would you do that using slot/web components/stencilJS?

I can get the Host Element of my web component in Stencil using

@Element() hostElement: HTMLElement;

I use my component like

<my-custom-component>
  <button>1</button>
  <button>2</button>
  <button>3</button>
</my-custom-component>

I want to render something like

render() {
  return slottedChildren ?
    <span>No Elements</span> :
    <ul class="my-custom-component">
      slottedChildren.map(child => <li class="my-custom-element>{child}</li>)
    </ul>;
}

Kind regards

like image 999
Schadenn Avatar asked Sep 20 '18 09:09

Schadenn


People also ask

What is the purpose of a web page component?

Web Components is a suite of different technologies allowing you to create reusable custom elements — with their functionality encapsulated away from the rest of your code — and utilize them in your web apps.

How do you build a web component?

Web components are a collection of open source technologies such as JavaScript and HTML that allow you to create custom elements that you can use and reuse in web apps. The components you create are independent of the rest of your code, so they're easy to reuse across many projects.

What are the basic components of web technologies?

Web components consist of three main technologies: HTML template. Custom elements. Shadow DOM.


1 Answers

Using slots you don't need to put a condition in your render function. You can put the no children element (in your example the span) inside the slot element and if no children are provided to the slot it will fall back to it. For example:

render() {
    return (
        <div>
            <slot><span>no elements</span></slot>
        </div>
    );
}

Answering the comment you wrote - you can do such a thing but with some coding and not out of the box. Every slot element has an assignedNodes function. Using that knowledge and the understanding of Stencil component life cycle you can do something such as:

import {Component, Element, State} from '@stencil/core';

@Component({
    tag: 'slotted-element',
    styleUrl: 'slotted-element.css',
    shadow: true
})
export class SlottedElement {
    @Element() host: HTMLDivElement;
    @State() children: Array<any> = [];

    componentWillLoad() {
        let slotted = this.host.shadowRoot.querySelector('slot') as HTMLSlotElement;
        this.children = slotted.assignedNodes().filter((node) => { return node.nodeName !== '#text'; });
    }

    render() {
        return (
            <div>
                <slot />
                <ul>
                    {this.children.map(child => { return <li innerHTML={child.outerHTML}></li>; })}
                </ul>
            </div>
        );
    }
}

This is not an optimal solution and it will require that the style of the slot should have display set to none (cause you don't want to show it). Also, it will only work with simple elements that only need rendering and not requiring events or anything else (cause it only uses them as html string and not as objects).

like image 200
Gil Fink Avatar answered Sep 23 '22 19:09

Gil Fink