En este artículo aprenderás a realizar repeticiones en templates de Lit. Veremos que usamos la función map() de los arrays, lo que nos permite realizar un bucle y generar un array de templates que se renderizarán en el componente.
Hasta este momento en el Manual de Lit hemos realizado interpolación de valores en templates, generalmente de propiedades del componente, pero también de sub-templates o valores devueltos por métodos en general. Sin embargo hay ocasiones en las que no queremos realizar una interpolación de un valor suelto, sino de todos los valores de un array.
Esto es algo muy normal en los componentes, porque en muchas ocasiones lo que tengo es una lista de valores que deben mostrarse en la vista del componente. Por ejemplo, una lista de productos relacionados, una lista de contactos, un conjunto de etiquetas y cosas similares.
Para mostrar todos los valores de una lista, necesitamos que dichos valores estén incorporados en un array y en el template simplemente haremos el recorrido de ese array. En este artículo vamos a aprender a realizarlo.
Podrías realizar también recorridos a propiedades de un objeto, aunque no es algo tan común, también sería posible, aunque necesitaríamos otro tipo de código de la que vamos a ver en este artículo.
Propiedades de tipo array
Primero vamos a conocer las propiedades de tipo array, que son las que podremos recorrer con un bucle en el template. Existe un "type" específico de Lit para las propiedades que contienen arrays. Las declararemos así:
static get properties() {
return {
tags: { type: Array },
};
}
Inicializar las propiedades de array
Por lo general, es importante que las propiedades de array se inicialicen siempre en el constructor. Es cierto que esa inicialización muchas veces se da mediante el valor de un atributo de la etiqueta del componente, pero no es suficiente por dos motivos:
- El valor que nos envíen vía atributo puede llegar después que el componente en sí se haya dibujado en la página. Si no hemos inicializado el valor de la propiedad tipo array, aunque sea a un array vacío, cuando lo intentemos recorrer con un bucle nos dará un error en tiempo de ejecución porque no se puede recorrer un valor "undefined".
- Tampoco nos podemos fiar que siempre inicialicen el array pasando los valores mediante el atributo y queremos que el componente siga funcionando en el caso que no haya ninguna carga del valor en el atributo.
Como digo, el mínimo sería que en el constructor inicialicemos la propiedad de array a un arreglo vacío, tal como se puede ver en este código.
constructor() {
super();
this.tags = [];
}
Por supuesto, si tenemos valores de antemano y queremos usarlos para inicializar el array con ellos, también podemos hacerlo.
constructor() {
super();
this.tags = ['Javascript', 'Lit', 'Web Components', 'Custom Elements', 'Array'];
}
Método map() de los arrays Javascript
El método map()
es un método existente en todo array de Javascript. Es algo nativo, del lenguaje, no específico de Lit. Este método sirve para recorrer un array y crear un array distinto, resultado generalmente de la transformación de los valores que había en el array original.
Vamos a ver este código:
var elDoble = [1,2,3,4].map( valor => valor * 2);
console.log(elDoble);
Al ejecutarse veremos en la consola de Javascript el array llamado "elDoble" generado por el método map()
, que contendrá los valores [2, 4, 6, 8]
.
Lo que hace map()
es ejecutar una función que le enviamos por parámetro, tantas veces como elementos tiene el array. Esa función recibe en cada iteración por parámetro el valor del ítem actual y tiene que devolver otro valor. Generalmente el valor que devuelve será una transformación del valor original, por ejemplo en el código anterior lo que se hace es multiplicar por 2 ese valor.
El array que fabrica el método map()
contendrá los valores que haya devuelto la función para cada una de las iteraciones, que ha calculado a partir de cada uno de los valores del array original.
Usando el método map para hacer recorridos a arrays en templates Lit
El método map()
de los arrays de Javascript lo podemos usar dentro de un template de Lit. Lo que realizará será el recorrido a los elementos del array, creando otro array que será lo que se muestre en el template.
Supongamos que quiero hacer un recorrido a un array de elementos que tengo la propiedad this.tags
, para mostrar cada uno de esos elementos en una lista. Entonces usaré un código como este:
render() {
return html`
<ul>
${this.tags.map( tag => html`<li>${tag}</li>`)}
</ul>
`;
}
Como puedes ver, lo que hacemos es interpolar el resultado de la ejecución de this.tags.map()
. Ese método map() devuelve es un array de templates, en el que encontramos la etiqueta LI
y dentro el nombre del tag actual. Por tanto, en nuestra vista aparecerá una lista UL
con tantos LI
como elementos había en el array this.tags
.
El componente completo de ejemplo de un recorrido sencillo a un array lo puedes ver a continuación.
import { LitElement, html, css } from 'lit';
export class DwTagList extends LitElement {
static styles = [
css`
:host {
display: block;
}
`
];
static get properties() {
return {
tags: { type: Array },
};
}
constructor() {
super();
this.tags = ['Javascript', 'Lit', 'Web Components', 'Custom Elements', 'Array'];
}
render() {
return html`
<ul>
${this.tags.map( tag => html`<li>${tag}</li>`)}
</ul>
`;
}
}
customElements.define('dw-tag-list', DwTagList);
Cambios reactivos en arrays ¿cómo y cuándo?
Igual que cualquier dato que se usa en los templates, el mapeo de las propiedades de array para su recorrido también es reactivo. De este modo, cuando cambia el array el template se vuelve a dibujar, presentando los valores tal como están en el array. Sin embargo, hay unas consideraciones importantes que mencionar.
La información que viene a continuación es muy importante, puesto que es una de las fuentes de problemas habituales que las personas que se inician con Lit pueden encontrarse.
La regla que debemos conocer es esta: Solamente se produce el cambio reactivo cuando el array es nuevo, es decir, es la referencia a otro array. No se producirán cambios en el template si solamente cambia una de las casillas del array.
Dicho de otra manera, para que el cambio sea reactivo se tiene que asignar un nuevo array a la propiedad. No se producen comportamientos reactivos cuando cambia una de las casillas internas del array.
Por ejemplo, esto cambiaría todo el array:
this.tags = ['nuevo','array'];
Pero no cambiará el template si hacemos algo como esto:
this.tags[0] = 'otra cosa';
Si queremos introducir un nuevo elemento en el array, tampoco se actualizaría el template si hacemos esto:
this.tags.push('Frontend');
Este comportamiento es simplemente por motivos de rendimiento. Ocurre parecido con las propiedades de objetos. Por eso es recomensable que usemos objetos y arrays como propiedades inmutables. Si deseamos que cambien, entonces tenemos que crear un nuevo array u objeto.
Entonces ¿Cómo puedo introducir un elemento en el array de modo que se provoque el repintado del template? Simplemente asignando un array nuevo, lo que puedes conseguir de diversas maneras, una de las más cómodas sería usando el operador de propagación:
this.tags = [...this.tags, 'Frontend'];
Cómo mostrar un mensaje cuando no hay elementos en el array
Es muy normal que a medida que el usuario hace cosas con el componente el array vaya cambiando y por tanto se realicen redibujados en el template. También es perfectamente posible que algunas veces el array esté simplemente vacío, en cuyo caso podríamos mostrar un mensaje como "Este elemento no tiene tags" o cualquier cosa así.
Esto lo conseguimos con un simple condicional en el template, tal como aprendimos en el artículo anterior.
render() {
return html`
${this.tags.length == 0
? html`<p>No tenemos tags que mostrar</p>`
: html`
<ul>
${this.tags.map( tag => html`<li>${tag}</li>`)}
</ul>
`
}
`;
}
Como puedes apreciar, solamente necesitamos comprobar si la propiedad "length" del array es igual a cero. En cuyo caso indicamos que no tenemos elementos que mostrar. En caso contrario, entonces realizamos el recorrido al array.
Conclusión
Con esto hemos aprendido a realizar repeticiones en templates, lo que es muy habitual en el desarrollo de componentes. Es importante volver a mencionar que para realizar esta tarea nos apoyamos en el lenguaje Javascript, usando el método map() existente en los arrays.
De hecho, estar tan cerca del lenguaje es una de las ventajas de Lit. Casi todo lo hacemos con Javascript nativo, por eso ocupa tan poco la librería. Además, por este mismo motivo, por estar tan pegados a los estándares, si algún día tenemos que cambiar de librería, los componentes necesitarán pocas actualizaciones.
Puedes encontrar el código de este componente y el resto del proyecto, tal como hemos ido avanzando en el manual en este repo de GitHub.
Miguel Angel Alvarez
Fundador de DesarrolloWeb.com y la plataforma de formación online EscuelaIT. Com...