> Manuales > Manual de Lit

Explicamos el método firstUpdated() que ofrece la librería Lit para ejecutar acciones cuando el componente ya está completamente montado en la página, con el template listo en el Shadow DOM y las propiedades completamente inicializadas.

Usando método firstUpdated del ciclo de vida en Lit

En este artículo vamos a conocer y practicar con otro de los métodos básicos del ciclo de vida de los componentes en Lit. En el anterior artículo aprendimos algunos métodos del ciclo de vida que nos proporciona el estándar Javascript y en esta ocasión hablaremos de un método específico introducido por Lit.

Para expliar el método firstUpdated del ciclo de vida en Lit hemos dividido el tema en los siguientes apartados de interés.

Método firstUpdated() de Lit

El método firstUpdated sirve para realizar tareas de inicialización del componente. En ese sentido sería parecido al método constructor, pero tiene una diferencia fundamental que vamos a estudiar.

El método constructor también pertenece al ciclo de vida de los componentes en el estándar Web Components. Se ejecuta en la inicialización del objeto, pero su ejecución se produce antes que firstUpdated. El constructor de la clase del componente se ejecuta apenas el componente se está creando en la memoria de Javascript, independientemente de si ese elemento se ha situado o no en el DOM de la página. Por tanto, en el momento de la ejecución del constructor hay muchas cosas que todavía no están listas, como por ejemplo la renderización del template.

En cambio firstUpdated es un método que se ejecuta en un estado más "maduro" del componente, en concreto cuando el componente ya se está mostrando en la página y todo su template ya es visible y, además, las propiedades del componente ya están volcadas en el template con sus valores actuales.

Por tanto, cuando se ejecuta firstUpdated podemos estar seguros que:

Por todo ello, firstUpdated es un excelente método para realizar acciones que se ejecutarán una vez sobre el componente, generalmente de inicialización, pero que tengan que derivarse al momento en el que el DOM del componente ya ha sido creado.

Ejemplo de uso del método firstUpdated

Para ilustrar el uso del método firstUpdated vamos a construir un componente de caja de confirmación (checkbox) que tendrá un estilo distinto, con un diseño personalizado.

Las cajas de input checkbox no permiten grandes cambios de estilos mediante su CSS, o por lo menos los cambios posibles son bastante limitados, por lo que un componente personalizado podría aportar más versatilidad en lo que respecta al aspecto.

De hecho, existen publicados ya componentes que hacen una caja input checkbox, pero de manera personalizada. Vamos a usar uno de ellos que se llama dile-checkbox, que está publicado en la biblioteca de componentes Dile.

Sin embargo solamente vamos a apoyarnos en ese elemento checkbox, realizando una mejora fundamental para que lo podamos usar fácilmente dentro de un formulario.

Shadow DOM y formularios HTML

Pero antes de explicar por qué necesitamos una mejora sobre el componente dile-checkbox tengo que comentar un caso importante con los web components que afecta a los formularios. Se trata del shadow DOM y los formularios en realidad.

Todo campo (input o similar) que esté en el shadow DOM no se envía al enviar un formulario de manera común. Es decir, si tienes un formulario que tiene dentro un componente y ese componente usa en su shadow DOM uno o varios campos de formulario, éstos no se enviarán automáticamente al enviar el formulario por las vías normales.

Es decir, al enviarse el formulario el navegador desconsidera todos los campos input que haya en el shadow DOM. Por ello, en el caso de elementos de formulario que usemos desde un web component, tenemos que saber que, si no hacemos nada, no los podremos usar en formularios comunes.

Tengo entendido que es una situación que se va a poder solucionar con las mejoras que están planificadas para el estándar de Web Components, pero todavía no tengo novedades que esto haya cambiado.

¿Cómo lo podemos solucionar? Existirían varias formas, pero una de ellas bastante sencilla consistiría en añadir en el light DOM o DOM local del componente un campo de formulario que sí se enviará, por lo estar en el shadow DOM. Esto lo podemos hacer fácilmente con Javascript en la inicialización del componente y solo nos quedaría aplicar un poco de CSS para que ese campo del light DOM del componente no se vea, o simplemente usar un campo hidden de formulario que serviría igualmente.

Esta es justamente la mejora que voy a aplicar sobre este campo dile-checkbox, que nos permitiría usar el elemento checkbox en cualquier formulario sin tener ese problema descrito del shadow DOM y los formularios.

Por qué necesito usar firstUpdated

Lo que vamos a hacer entonces es generar dinámicamente un elemento en el light DOM del componente y lo vamos a hacer en el momento del ciclo de vida del firstUpdated.

El motivo es porque en este componente necesito configurar algunas cosas sobre el elemento checkbox, como el "name" del elemento de formulario que se tendrá que generar, así como trasladar el estado del input, para que sea checked o no. Todas esas configuraciones dependen de las propiedades que nos hayan pasado al usar el componente.

Recordemos que, hasta que no se ejecute firstUpdated, no puedo estar seguro que los valores de las propiedades han sido sincronizados con los valores definidos en los atributos del componente.

Así usaremos el componente:

<dw-input-checkbox name="checkboxcomun" checked>Estoy checked de <i>inicio</i></dw-input-checkbox>

En ese ejemplo el checked debe aparecer marcado y además el name del elemento (tal como se enviará al enviar el formulario debe ser "checkboxcomun".

Para crear dinámicamente el campo input checkbox en el light DOM debemos hacerlo en el momento en el que los atributos de la etiqueta host han sido usados para inicializar las propiedades del componente y ese momento es justamente en el que se ejecuta firstUpdated.

Código del componente que usa firstUpdated

Ahora vamos a ver el código de nuestro componente completo, para ver cómo hemos usado el método firstUpdated.

import { LitElement, html, css } from 'lit';
import '@dile/dile-checkbox/dile-checkbox';

export class DwInputCheckbox extends LitElement {
    static styles = [
        css`
            :host {
                display: block;
            }
            ::slotted(input) {
                display: none;
            }

        `
    ];

    static properties = {
        checked: { type: Boolean },
        name: { type: String },
        disabled: { type: Boolean },
    }

    render() {
        return html`
            <dile-checkbox 
                id="elcheck"
                name="${this.name}" 
                ?disabled=${this.disabled}
                ?checked=${this.checked}
                @click=${this.clickHandler}
            ><slot></slot></dile-checkbox>
        `;
    }

    firstUpdated() {
        this.elcheck = this.shadowRoot.getElementById('elcheck');
        this.input = document.createElement('input');
        this.input.setAttribute('type', 'checkbox');
        this.input.setAttribute('name', this.name);
        this.input.checked = this.checked;
        //this.input.
        this.appendChild(this.input);
    }

    clickHandler() {
        console.log('this.elcheck.checked', this.elcheck.checked);
        this.input.checked = this.elcheck.checked;
    }
}
customElements.define('dw-input-checkbox', DwInputCheckbox);

Este componente tiene algunos detalles interesantes que vamos a resumir.

Usar otros componentes

Primero estamos usando otro componente, por lo que tendremos que instalarlo e importarlo al principio del código.

Para instalar el componente dile-checkboxen un proyecto frontend tendremos que lanzar el siguiente comando de npm:

npm install @dile/dile-checkbox

Luego se importa gracias a la siguiente línea de código que colocamos al iniciar el módulo del componente, en los imports.

import '@dile/dile-checkbox/dile-checkbox';

El uso del componente lo hacemos dentro del template, en el método render.

render() {
    return html`
        <dile-checkbox 
            id="elcheck"
            name="${this.name}" 
            ?disabled=${this.disabled}
            ?checked=${this.checked}
            @click=${this.clickHandler}
        ><slot></slot></dile-checkbox>
    `;
}

Puedes ver que hemos incorporado el bindeo a un par de propiedades boleanas, que se hace con la sintaxis del interrogante.

Además observarás que le hemos aportado un identificador al dile-checkbox, para luego poder referirnos a él mediante Javascript.

También hemos añadido un manejador de eventos con @click para poder reaccionar cuando el elemento se pulse y cambie de estado.

Uso del método firstUpdated

Quizás la parte que más nos interese sea la del firstUpdated. En ella hacemos todo el trabajo de generación de un elemento del HTML mediante código Javascript y vemos también cómo añadirlo al light DOM del componente con el método nativo appendChild.

firstUpdated() {
    this.elcheck = this.shadowRoot.getElementById('elcheck');
    this.input = document.createElement('input');
    this.input.setAttribute('type', 'checkbox');
    this.input.setAttribute('name', this.name);
    this.input.checked = this.checked;
    this.appendChild(this.input);
}

Todos los métodos como createElement, setAttribute y appendChild son pertenecientes al API de manejo del DOM de Javascript, por lo que seguramente ya los conozcas si eres desarrollador frontend. Si no es así, conviene estudiar un poco más sobre cómo manipular el DOM con Javascript en el manual Desarrollo en Javascript del lado del cliente.

Después de ejecutarse este método firstUpdated() tendremos dos propiedades no reactivas del componente:

Estas variables que hemos asociado a "this" son por tanto propiedades de la clase que implementa el componente pero no son propiedades reactivas porque no las hemos declarado en el objeto "properties". Obviamente, no nos interesaba que fueran reactivas y no harán falta declararlas por tanto.

Cómo actualizar el elemento del light DOM cuando cambia estado del componente

En lo que respecta al Javascript nos queda por ver el efecto de cambiar el estado del input insertado. Eso lo conseguimos cuando se hace clic sobre el elemento checkbox, al cual insertamos un manejador declarado en el template con @click, que vemos su implementación aquí.

clickHandler() {
    this.input.checked = this.elcheck.checked;
}

Fíjate que usamos las dos propiedades de la clase, this.input y this.elchecked, traspasando el valor de "checked" de una para la otra.

Ocultar el elemento input checkbox

Solo nos quedaría ocultar el input-checkbox ya que no queremos que aparezca por ningún lugar, pues es solamente un control oculto que nos permite que el valor del checkbox se traslade en un hipotético envío de formulario.

static styles = [
    css`
        :host {
            display: block;
        }
        ::slotted(input) {
            display: none;
        }
    `
];

Esto lo conseguimos con el selector ::slotted() que habíamos aprendido en un artículo anterior. Simplemente indicamos que los campos input que sean hijos directos de la etiqueta host deben de estar ocultos (display none).

Miguel Angel Alvarez

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

Manual