> Manuales > Manual de Lit

Qué son los slots, por qué son importantes en el desarrollo de custom elements del estándar de Web Components. Cómo crear slots por medio de la etiqueta del componente y usarlos en el template con Lit.

Trabajar con slots en Web Components y Lit

Cuando trabajamos con Web Components es muy importante hacer un buen uso de lo que llamamos slots. En este artículo del Manual de Lit te los vamos a explicar y los pondremos en práctica con nuevos componentes.

Qué son los slots

Slot es un concepto común del desarrollo con web components. Consisten en una especie de cajones donde podemos colocar código HTML cuando usamos los componentes. Ese código HTML de los slots luego lo podremos reutilzar dentro del componente, permitiendo un nuevo grado de personalización. Para usar los slots disponemos de una etiqueta <slot> que se podrá usar en los templates Lit que nos servirá para volcar el contenido de los distintos cajones en el lugar preciso dentro del componente.

Esta sería una descripción general, pero no te preocupes si no la entiendes del todo pues lo vamos a explicar de una manera más detallada a lo largo del artículo, con ejemplos que te ayudarán a entenderlo mejor.

Por qué necesitamos slots

Cuando usamos un componente la etiqueta host puede venir vacía, sin ningún contenido entre la etiqueta de inicio y de cierre del componente. Por ejemplo así:

<dw-age birthday="2000-01-12"></dw-age>

Pero también hay otros componentes, incluso algunos que hemos desarrollado ya en el Manual de Lit que tienen contenido.

<dw-button>Enviar</dw-button>

Pues bien, los slots no son más que enganches que permiten traernos el contenido hijo de la etiqueta del componente, para mostrarlo dentro del template.

Cómo usar los slots

Hasta el momento hemos creado un par de componentes que tenían slots. Por ejemplo el dw-botton. Para usar el contenido del slot simplemente colocamos la etiqueta <slot> en el lugar donde queremos que ese contenido aparezca. Puedes verificarlo en el template del componente mencionado.

Por tanto, un componente puede tener toda una serie de marcado y, entre medias de ese marcado puedes querer mostrar el contenido hijo de la etiqueta host, en cuyo caso pones el slot.

render() {
    return html`
        <div>Esto es contenido del shadow dom del componente</div>
        <div><p>Aquí voy a usar el slot: <slot></slot></p></div>`;
}

Otra cosa interesante, si en el template de los componentes no llegamos a usar el slot para nada, simplemente no se verá pon ningún lado. Por ejemplo, el componente dw-age no usa el slot por lo que, si hubiese contenido dentro, no se vería en la página.

<dw-age>Este contenido no lo uso para nada y no aparecerá.</dw-age>

Contenido predeterminado para los slot

Generalmente, la etiqueta <slot> la colocarás sin contenido, pero también es posible crear un contenido predeterminado, que se usará cuando no se haya entregado un slot con el que rellenar esa posición. Para ello simplemente entregamos un contenido en la etiqueta <slot>.

<slot>Este contenido se mostrará si no se ha incluido el slot al usar el componente</slot>

Shadow DOM vs slots

Como hemos mencionado en alguna ocasión, todo el template de los componentes se aloja dentro del Shadow DOM del componente. El Shadow DOM lo podemos considerar como un DOM interno al componente, que tiene la particularidad de estar encapsulado, por lo que:

Por supuesto el propio componente dispone de propiedades y métodos que pueden manipular el Shadow DOM. En realidad desde fuera no se puede manipular el Shadow DOM, por lo que utilizamos las funciones del API del componente (sus métodos y las propiedades públicas, así como los atributos de la etiqueta host) para conseguir interaccionar con el componente. Será el propio componente el que internamente manipule su propio Shadow DOM. Como esa manipulación es responsabilidad del propio componente éste tiene que ofrecer un API suficiente para que se pueda interaccionar desde fuera.

Pues bien, es importante saber que los slots no forman parte del Shadow DOM, sino que es contenido que depende directamente de la propia página que usa el componente, o de otro componente que lo esté usando. Dicho de otro modo, todo lo que pongamos dentro de la etiqueta del componente es contenido del padre del componente y no del componente en sí.

Esto es importante porque impacta directamente en varios factores.

Para referirnos al contenido de la etiqueta del componente, lo que se puede usar mediante slot, a veces utilizamos el término "DOM Local", "Light DOM", o simplemente hijos del componente.

Cómo usar varios slots

En este artículo de introducción a slots vamos a terminar con una práctica nueva que no hemos visto todavía, que nos permite reutilizar el contenido de los hijos del componente por partes, en vez de usarlo todo en un bloque.

Para ello vamos a mejorar sensiblemente uno de los componentes que habíamos desarrollado ya en este manual: dw-show-hide que básicamente mostraba u ocultaba un contenido según el usuario hiciese clic en una parte del componente.

El componente dw-show-hide lo comenzamos a desarrollar en el artículo Propiedades reflect en Lit.

Ahora queremos conseguir que el contenido que queremos mostrar y ocultar esté presentado como contenido hijo del componente. Pero además, queremos tener dos contenidos que representaremos por separado en dos puntos distintos del template.

Para entender esto vamos a fijarnos primero en el HTML que usaremos para este componente:

<dw-show-hide>
    <h2 slot="trigger">Qué es Lit</h2>
    <div slot="content">
        <p>Es una librería ligera para el desarrollo de custom elements basados en el estándar de Web Components, con alto rendimiento y una experiencia de desarrollo avanzada.</p>
    </div>
</dw-show-hide>

Como puedes apreciar, tenemos dos etiquetas hijo en el DOM local del componente, un h2 y un div. Ambas tienen un atributo nuevo: slot.

El atributo slot es fundamental para nuestro objetivo de separar los slots en dos bloques y nos sirve para nombrar e identificar cada uno de ellos. Gracias a ese atributo podemos ahora usar los slots por separado. Para ello seguimos trabajando con la etiqueta <slot>, pero ahora le pondremos a ésta un atributo "name" para indicar cuál de los dos slots queremos volcar en esa parte del template.

<slot name="trigger"></slot>

Así, ahora podríamos tener varias etiquetas slot en el template del componente, como podemos ver en el siguiente código.

render() {
    return html`
        <main>
            <section class="trigger" @click=${this.change}>
                <div>
                    <slot name="trigger"></slot>
                </div>
                <span>
                    <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6-6-6z"/></svg>
                </span>
            </section>
            <section class="content">
                <slot name="content"></slot>
            </section>
        </main>
    `;
}

En un lado usamos el "trigger" y en el otro usamos el "content". Por supuesto, estos nombres que hemos aplicado a nuestros slots son totalmente arbitrarios, es decir, son los que hemos querido ponerles y nada más. Puedes elegirnos tú como mejor te venga.

Código completo del componente dw-show-hide mejorado

Vamos a ver el código completo de este nuevo componente que usa varios slots, para que lo tengas como referencia. Tiene alguna cosilla interesante, como un icono que se anima cada vez que se muestra el contenido o se oculta.

import {LitElement, html, css} from 'lit';

class DwShowHide extends LitElement {

    static styles = css`
        :host {
            display: block;
        }
        main {
            border: 1px solid #ddd;
            padding: 10px;
        }
        .content {
            display: none;
        }
        .trigger {
            display: flex;
            align-items: center;
            justify-content: space-between;
        }
        .trigger div {
            flex-grow: 1;
        }
        svg {
            width: 52px;
            height: 52px;
            transition: transform 0.3s linear;
        }
        :host([show]) .content {
            display: block;
        }
        :host([show]) svg {
            transform: rotate(90deg);
        }
    `;

    static properties = {
        show: { 
            type: Boolean,
            reflect: true,
        },   
    }

    constructor() {
        super();
        this.show = false;
    }

    render() {
        return html`
            <main>
                <section class="trigger" @click=${this.change}>
                    <div>
                        <slot name="trigger"></slot>
                    </div>
                    <span>
                        <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6-6-6z"/></svg>
                    </span>
                </section>
                <section class="content">
                    <slot name="content"></slot>
                </section>
            </main>
        `;
    }

    change() {
        this.show = !this.show;
    }
}

customElements.define('dw-show-hide', DwShowHide);

Conclusión a la introducción a los slots en Lit

Este ha sido un artículo bastante completo sobre los slots en Web Components y la micro-librería Lit, en el que hemos visto qué son y cómo los podemos usar en nuestros componentes, incluso cómo podemos manejar varios slots con nombre en un mismo componente.

No obstante nos queda bastante por hablar de Slots, por lo que vamos a realizar en adelante otros artículos en los que ampliaremos más la información, como explicar cómo los slots afectan positivamente al SEO de las páginas o cómo aplicar CSS a los slots desde dentro del componente.

Miguel Angel Alvarez

Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...

Manual