Insert content into Webcomponents template

Asked

Viewed 38 times

0

I’m doing a Webcomponent and I came across a situation: Content is not inside the component template.

If you inspect below, the "Alert Test" is positioned below the template instead of inside. I searched about slots but didn’t understand how to use it.

I ask for help to clarify the use and if I am doing something wrong. Thank you!

class Alert extends HTMLElement {

    constructor() {
        super();

        this.shadow = this.attachShadow({ mode: 'open' });
        this._priority = null;
        this._typeClass = '';
        this._iconClass = '';
        this._alertTypeName = '';
        this._alertPriority = null;
    }

    get type() {
        return this.getAttribute('type');
    }

    get priority() {
        return this.getAttribute('priority');
    }

    static get observedAttributes() {
        return ['type', 'priority'];
    }

    attributeChangedCallback(type, oldVal, newVal) {
        console.log(this.shadow)
    }

    connectedCallback() {
        switch (this.type) {

            case "success":
                this._alertTypeName = "Sucesso";
                this._typeClass = "alert-success";
                this._iconClass = "glyphicon-ok";
                this._alertPriority = this.priority;
                break;

            case "info":
                this._alertTypeName = "Informação";
                this._typeClass = "alert-info";
                this._iconClass = "glyphicon-info-sign";
                this._alertPriority = this.priority;
                break;

            case "warning":
                this._alertTypeName = "Aviso";
                this._typeClass = "alert-warning";
                this._iconClass = "glyphicon-warning-sign";
                this._alertPriority = this.priority;
                break;

            case "error":
                this._alertTypeName = "Erro";
                this._typeClass = "alert-danger";
                this._iconClass = "glyphicon-exclamation-sign";
                this._alertPriority = "alert";
                break;
        }

        var template = `
            <div class="alert alert-dismissible fade show" role>
            <span class="glyphicon" aria-hidden="true"></span>
            <button type="button" class="close" data-dismiss="alert" aria-label="Fechar"><span aria-hidden="true">&times;</span></button>
            </div>
    `;

        this.shadow.innerHTML = template;
        var alertElement = this.shadow.querySelector('.alert');

        alertElement.setAttribute("role", this._alertPriority);
        alertElement.children[0].setAttribute("aria-label", this._alertTypeName);
        alertElement.classList.add(this._typeClass);
        alertElement.children[0].classList.add(this._iconClass);
    }

}

window.customElements.define('meu-alert', Alert);
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>



<meu-alert type="success" priority="alert">
Teste de alerta
</meu-alert>

1 answer

1


One of the ways to add content to a custom element is to use the element <slot>

This element is part of the technology of Web Components of Javascript. With it it is possible to replace or add a content from <template>.

There are two ways to use the above element:

  • Enter the attribute name
  • Empty attribute name

When setting a name for the <slot>, we can have multiple of these to create a structure. For example:

<!-- Template -->
<template>
  <div>
    <slot name="header"></slot>
    <slot name="content"></slot>
    <slot name="footer"></slot>
  </div>
</template>

<!-- No código -->
<custom-element>
  <header slot="header">...</header>
  <section slot="content">...</section>
  <footer slot="footer">...</footer>
</custom-element>

Leaving the attribute name empty, we will be defining that the slot will be the default. That is, all content (if any) within the custom element will override the default content of slot.

Practical example:

class Alert extends HTMLElement {

    constructor() {
        super();

        const newTemplate = document.createElement('template')
        newTemplate.innerHTML = `
          <style>@import url('https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css')</style>

          <div class="alert alert-${this.type} alert-dismissible fade show" role="alert">
            <slot></slot>
          </div>
        `
        
        const nodes = newTemplate.content.cloneNode(true)
        
        this.attachShadow({ mode: 'open' });
        this.shadowRoot.appendChild(nodes);
    }

    get type () {
        let type = this.getAttribute('type');
        
        return !!['success', 'danger'].includes(type)
          ? type
          : 'primary'
    }

    static get observedAttributes() {
        return ['type'];
    }
}

window.customElements.define('meu-alert', Alert);
<meu-alert type="danger">
  Teste de alerta
</meu-alert>

Alternative

The second and easiest alternative is to capture the custom element content through the property this.innerHTML. With that, the Javascript will return what was informed in the content of the custom element.

class Alert extends HTMLElement {

    constructor() {
        super();
        
        let slotFictitious = this.innerHTML

        const newTemplate = document.createElement('template')
        newTemplate.innerHTML = `
          <div class="alert alert-${this.type} alert-dismissible fade show" role="alert">
            ${slotFictitious}
          </div>
        `
        
        const nodes = newTemplate.content.cloneNode(true)
        
        this.innerHTML = nodes.firstElementChild.outerHTML
    }

    get type () {
        let type = this.getAttribute('type');
        
        return !!['success', 'error'].includes(type)
          ? type
          : 'primary'
    }

    static get observedAttributes() {
        return ['type'];
    }
}

window.customElements.define('meu-alert', Alert);
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>

<meu-alert type="success">
  Teste de alerta 2
</meu-alert>

  • Wonderful guy, clarified a lot for me! Thank you!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.