Gairebé cap interfície real mostra sempre exactament el mateix contingut: una tasca pot estar pendent, en progrés o feta, i probablement vulguis mostrar una insígnia diferent segons quin sigui el seu estat. A la lliçó anterior ja vas utilitzar, de passada, un operador ternari dins d'una interpolació; en aquesta lliçó aprofundiràs en les diferents formes de renderitzar contingut condicionalment a Lit, quan convé cadascuna, i les aplicaràs per donar a <task-card> una insígnia d'estat que canvia segons les dades de la tasca.
Contingut
- Renderitzat condicional: no és màgia, és JavaScript
- L'operador ternari: l'opció més habitual
- L'operador
&&: mostrar o amagar sense alternativa - Extreure la lògica a funcions auxiliars
- Compte amb els "falsy" en utilitzar
&& - La directiva
when: una menció de passada - Aplicant-ho a
<task-card>: insígnia segons l'estat
- Renderitzat condicional: no és màgia, és JavaScript
Una de les idees més importants que cal interioritzar sobre Lit és que no existeix una sintaxi especial de plantilles per a condicionals, a l'estil {% if %} d'altres motors de plantilles que potser coneixes d'altres contextos. Lit no necessita inventar una sintaxi pròpia perquè, com vas veure a la lliçó "El Motor de Plantillas de Lit", dins de ${} es pot escriure qualsevol expressió JavaScript vàlida, i JavaScript ja disposa d'operadors capaços d'expressar una condició en forma d'expressió: l'operador ternari i l'operador &&.
Aquesta és, de fet, una de les senyes d'identitat del disseny de Lit: en lloc de crear un llenguatge de plantilles paral·lel amb les seves pròpies regles a aprendre, reutilitza JavaScript tal com és. Qui ja sap JavaScript ja sap, sense adonar-se'n, gran part de "la sintaxi de condicionals de Lit".
- L'operador ternari: l'opció més habitual
L'operador ternari (condició ? valorSiVertader : valorSiFals) és la forma més comuna de renderitzar condicionalment a Lit quan hi ha dues alternatives possibles, totes dues amb contingut a mostrar:
El resultat de cada branca del ternari pot ser, igual que en qualsevol interpolació, text simple, un número, o fins i tot una altra plantilla html aniuada (com es va veure a la lliçó anterior):
render() {
return html`
<p>
${this.completada
? html`<span class="icono-ok">✓</span> Finalizada`
: html`<span class="icono-pendiente">○</span> Pendiente`}
</p>
`;
}Aquí cada branca del ternari retorna una plantilla html diferent, amb el seu propi <span> amb una classe diferent. Lit gestiona això sense cap problema: cada vegada que render() s'executa, avalua la condició i col·loca al DOM el resultat de la branca corresponent, substituint el que hi hagués abans si la branca activa ha canviat des del renderitzat anterior.
- L'operador
&&: mostrar o amagar sense alternativa
&&: mostrar o amagar sense alternativaQuan només interessa mostrar alguna cosa si es compleix una condició, i no hi ha cap contingut alternatiu a mostrar en cas contrari, el patró habitual és l'operador lògic &&:
render() {
return html`
<article>
<h3>${this.titulo}</h3>
${this.urgente && html`<p class="aviso">⚠ Esta tarea es urgente</p>`}
</article>
`;
}Aquest patró es basa en com avalua JavaScript l'operador &&: si l'operand de l'esquerra (this.urgente) és "fals", l'expressió completa s'avalua com aquell valor fals, sense arribar a avaluar l'operand de la dreta; si és "vertader", l'expressió completa pren el valor de l'operand de la dreta (la plantilla html de l'avís). Quan this.urgente és false, la interpolació completa val false, i Lit, en trobar false (o null, o undefined) com a resultat d'una interpolació de contingut, senzillament no renderitza res en aquest punt, sense deixar cap rastre al DOM.
Aquest patró és preferible al ternari quan no existeix una alternativa raonable a mostrar en el cas contrari; escriure this.urgente ? html\...` : ''funciona igual de bé, peròthis.urgente && html`...`` comunica amb més claredat la intenció de "mostrar això només si es compleix la condició, i res en cas contrari".
- Extreure la lògica a funcions auxiliars
Quan la lògica condicional comença a tenir més de dues o tres branques, o quan el càlcul de què mostrar és una mica més elaborat que una simple comparació, resulta molt més llegible extreure aquesta lògica a un mètode propi de la classe (una funció auxiliar) i cridar-lo des de dins de la interpolació:
import { LitElement, html } from 'lit';
class TaskCard extends LitElement {
constructor() {
super();
this.estado = 'en-progreso'; // 'pendiente' | 'en-progreso' | 'hecha'
}
renderIndicadorEstado() {
if (this.estado === 'hecha') {
return html`<span class="indicador ok">✓ Hecha</span>`;
}
if (this.estado === 'en-progreso') {
return html`<span class="indicador progreso">◐ En progreso</span>`;
}
return html`<span class="indicador pendiente">○ Pendiente</span>`;
}
render() {
return html`
<article>
<h3>${this.titulo}</h3>
${this.renderIndicadorEstado()}
</article>
`;
}
}Aquest patró —un mètode renderAlgo() que la mateixa classe crida des de render()— és extremadament habitual al codi Lit real, inclòs a la documentació oficial i en projectes grans. Permet utilitzar sentències completes de JavaScript (if, switch, bucles, variables intermèdies amb const) que no encaixarien directament dins d'un ${}, ja que allà només s'admeten expressions, com es va explicar a la lliçó anterior. L'únic requisit és que el mètode, igual que el mateix render(), acabi retornant un valor vàlid per interpolar: una plantilla html, text, un número, o null/undefined/false si no cal mostrar res.
- Compte amb els "falsy" en utilitzar
&&
&&El patró condició && html\...`vist a l'apartat 3 amaga una trampa clàssica de JavaScript que convé conèixer. L'operador&&no comprova estrictament si la condició éstrue; comprova si és "vertadera" en el sentit ampli de JavaScript (el que es coneix com "truthy"), i hi ha diversos valors que JavaScript considera "falsy" a més de false: 0, ''(cadena buida),null, undefinediNaN`.
El problema apareix quan la condició és un número que pot vàlidament valer 0:
render() {
return html`
<article>
<h3>${this.titulo}</h3>
${this.numeroDeComentarios && html`<p>${this.numeroDeComentarios} comentarios</p>`}
</article>
`;
}Si this.numeroDeComentarios val 0, l'expressió 0 && html\...`s'avalua com0(no com la plantilla), i aquí hi ha la trampa: Lit sí que renderitza el número0com a contingut de text, així que a la pantalla apareixerà literalment un0` solt, en lloc de no mostrar res. La solució habitual és forçar una comparació explícita que produeixi un booleà real:
Aquesta regla general convé recordar-la sempre que s'utilitzi && per a renderitzat condicional: si la condició és un número o una cadena que podria vàlidament ser 0 o '', cal convertir-la explícitament a un booleà (amb una comparació com > 0, o amb !!valor) abans d'utilitzar-la amb &&.
- La directiva
when: una menció de passada
when: una menció de passadaLit inclou, entre el seu catàleg de directives incorporades, una anomenada when que expressa d'una manera una mica més explícita la mateixa idea que un operador ternari:
import { when } from 'lit/directives/when.js';
render() {
return html`
${when(
this.completada,
() => html`<span>✓ Finalizada</span>`,
() => html`<span>○ Pendiente</span>`
)}
`;
}when rep la condició, una funció que produeix la plantilla per al cas vertader, i opcionalment una altra funció per al cas fals. El seu comportament, a la pràctica, és molt semblant al d'un operador ternari amb plantilles aniuades; la diferència principal és que les branques es passen com a funcions (el que retarda la seva execució fins que realment es necessiten) i que, per a qui llegeix el codi, deixa més explícita la intenció de "renderitzat condicional" com a concepte, davant d'un ternari que també podria estar seleccionant un simple valor no relacionat amb plantilles.
Aquesta directiva es menciona aquí només perquè la reconeguis si la trobes a la documentació oficial o en projectes de tercers; l'estudi de les directives de Lit en general —què són, com funcionen per dins i quan aporten valor davant JavaScript "a pèl"— és el contingut del mòdul 7, "Directives i Funcionalitats Avançades de Plantilles". Durant la resta d'aquest curs, i en particular en aquest mòdul, se seguirà utilitzant JavaScript estàndard (ternaris, &&, funcions auxiliars) com a tècnica principal, precisament perquè no requereix importar res addicional i demostra que el renderitzat condicional a Lit no depèn de cap funcionalitat especial.
- Aplicant-ho a
<task-card>: insígnia segons l'estat
<task-card>: insígnia segons l'estatAmb les tècniques anteriors ja es pot donar a <task-card> una insígnia d'estat que canviï el seu aspecte i el seu text segons la tasca estigui pendent, en progrés o feta. S'utilitzarà el patró de funció auxiliar de l'apartat 4, per ser el més llegible quan hi ha més de dues alternatives:
import { LitElement, html } from 'lit';
class TaskCard extends LitElement {
constructor() {
super();
this.titulo = 'Preparar la demo del sprint';
this.estado = 'en-progreso'; // 'pendiente' | 'en-progreso' | 'hecha'
this.urgente = false;
}
renderInsigniaEstado() {
if (this.estado === 'hecha') {
return html`<span class="insignia insignia--hecha">✓ Hecha</span>`;
}
if (this.estado === 'en-progreso') {
return html`<span class="insignia insignia--progreso">◐ En progreso</span>`;
}
return html`<span class="insignia insignia--pendiente">○ Pendiente</span>`;
}
render() {
return html`
<article>
<h3>${this.titulo}</h3>
${this.renderInsigniaEstado()}
${this.urgente && html`<p class="aviso">⚠ Urgente</p>`}
</article>
`;
}
}
customElements.define('task-card', TaskCard);Aquest component combina les tres tècniques vistes a la lliçó: una funció auxiliar (renderInsigniaEstado) que encapsula una lògica de tres branques mitjançant if normals (impossible d'expressar directament dins d'un únic ${}), i el patró && per mostrar l'avís d'urgència només quan correspon. Fixa't que les classes CSS (insignia--hecha, insignia--progreso...) s'interpolen aquí només com a referència de noms; la seva definició visual real —com es veuen aquests colors i icones— és contingut del mòdul 4, "Estils en Components Lit". De moment, sense aquests estils, la insígnia es veurà com a text sense format especial, i això és exactament l'esperat en aquest punt del curs.
Errors Comuns i Consells
- Utilitzar
&&amb condicions numèriques sense convertir a booleà: com s'ha vist a l'apartat 5,cantidad && html\...`pot mostrar un0solt a pantalla sicantidadval zero. Convé forçar sempre una comparació explícita (cantidad > 0`) en aquests casos. - Aniuar massa ternaris seguits: encadenar diversos ternaris (
a ? x : b ? y : c ? z : w) per representar més de dues alternatives és difícil de llegir. En quant hi hagi tres o més branques, és preferible extreure la lògica a una funció auxiliar ambif/switch, com es va fer a l'apartat 7. - Oblidar que retornar una cadena buida
''en una branca no és el mateix que no renderitzar res: encara que el resultat visual sigui el mateix (no apareix text), retornar''continua inserint un node de text buit al DOM, mentre que retornarnull,undefinedofalsefa que Lit no inserti res en absolut. A la pràctica aquesta diferència rarament importa, però convé conèixer-la per no sorprendre's en inspeccionar el DOM. - Repetir la mateixa condició diverses vegades a la plantilla: si necessites comprovar
this.estado === 'hecha'en diversos llocs d'una plantilla llarga, és un senyal que convé extreure aquesta lògica a una funció auxiliar o a una variable local calculada al principi derender(), en lloc de repetir la comparació.
Exercicis
- Afegeix a la funció
renderInsigniaEstado()un quart estat possible,'bloqueada', que mostri una insígnia amb el text "⛔ Bloqueada" i la classeinsignia--bloqueada. - Utilitzant el patró
&&amb la correcció de l'apartat 5, afegeix a<task-card>un campthis.numeroDeComentarios(inicialitza'l a0) i mostra un paràgraf "N comentarios" només quan aquest número sigui més gran que zero. Comprova manualment, canviant el valor des del constructor, que amb0no apareix cap "0" solt a pantalla. - Reescriu la insígnia d'estat de l'apartat 7 utilitzant la directiva
whenmencionada a l'apartat 6, consultant la seva signatura exacta a la documentació oficial de Lit (lit/directives/when.js), només amb finalitats de pràctica de lectura de documentació (no cal integrar-la a la resta del curs).
Solucions
renderInsigniaEstado() {
if (this.estado === 'hecha') {
return html`<span class="insignia insignia--hecha">✓ Hecha</span>`;
}
if (this.estado === 'en-progreso') {
return html`<span class="insignia insignia--progreso">◐ En progreso</span>`;
}
if (this.estado === 'bloqueada') {
return html`<span class="insignia insignia--bloqueada">⛔ Bloqueada</span>`;
}
return html`<span class="insignia insignia--pendiente">○ Pendiente</span>`;
}constructor() {
super();
// ...
this.numeroDeComentarios = 0;
}
render() {
return html`
<article>
<h3>${this.titulo}</h3>
${this.renderInsigniaEstado()}
${this.numeroDeComentarios > 0
? html`<p>${this.numeroDeComentarios} comentarios</p>`
: ''}
</article>
`;
}Amb this.numeroDeComentarios a 0, la condició this.numeroDeComentarios > 0 és false, així que no es mostra cap paràgraf ni cap "0" solt.
import { when } from 'lit/directives/when.js';
render() {
return html`
<article>
<h3>${this.titulo}</h3>
${when(
this.estado === 'hecha',
() => html`<span class="insignia insignia--hecha">✓ Hecha</span>`,
() => html`<span class="insignia insignia--pendiente">○ Pendiente / en progreso</span>`
)}
</article>
`;
}Aquesta solució es simplifica a només dues branques amb finalitats de pràctica; una rèplica exacta de les quatre branques de l'apartat 1 requeriria aniuar diverses crides a when, el que il·lustra precisament per què, amb més de dues alternatives, se sol preferir una funció auxiliar amb if/switch en lloc d'aniuar directives.
Conclusió
En aquesta lliçó has vist que el renderitzat condicional a Lit no requereix cap sintaxi especial: es basa directament en operadors estàndard de JavaScript, principalment l'operador ternari per triar entre dues alternatives i l'operador && per mostrar o amagar contingut sense alternativa, amb la cura de convertir explícitament a booleà les condicions que puguin valer 0 o cadena buida. També has aprés a extreure lògica condicional més complexa a funcions auxiliars dins de la mateixa classe, i has conegut de passada la directiva when, el seu estudi complet arribarà al mòdul 7. Amb tot això, <task-card> ja mostra una insígnia d'estat diferent segons la tasca estigui pendent, en progrés o feta, a més d'un avís condicional d'urgència.
A la següent lliçó, "Renderizado de Listas", faràs un pas més: en lloc de mostrar una única tasca, aprendràs a recórrer un array complet de tasques amb Array.map dins d'una plantilla, i utilitzaràs aquesta tècnica per construir <task-list>, el primer component de TaskFlow capaç de renderitzar diverses targetes <task-card> a partir d'una col·lecció de dades.
Curs de Lit
Mòdul 1: Introducció a Lit i Web Components
- Què són els Web Components i per què Lit?
- Configuració de l'Entorn de Desenvolupament
- El Teu Primer Component Lit
- Anatomia d'un Component Lit
Mòdul 2: Plantilles Reactives i Renderitzat
- El Motor de Plantilles de Lit
- Expressions i Interpolació en Plantilles
- Renderitzat Condicional
- Renderitzat de Llistes
- El Cicle de Renderitzat
Mòdul 3: Propietats i Estat Reactiu
- Propietats Reactives
- Estat Intern amb @state
- Tipus de Propietats i Conversors Personalitzats
- Atributs vs Propietats i Reflexió
Mòdul 4: Estils en Components Lit
- CSS Encapsulat amb Shadow DOM
- Estils Compartits entre Components
- Variables CSS Personalitzades i Theming
- Slots i Estilitzat de Contingut Distribuït
Mòdul 5: Esdeveniments i Comunicació entre Components
- Gestió d'Esdeveniments DOM en Plantilles
- Esdeveniments Personalitzats: Comunicació de Fill a Pare
- Comunicació de Pare a Fill amb Propietats
- Patrons de Comunicació entre Components Germans
Mòdul 6: Cicle de Vida i Comportament Avançat
- Callbacks del Cicle de Vida
- Hooks Reactius: willUpdate, updated i firstUpdated
- Controladors Reactius
- Mixins i Composició de Comportament
Mòdul 7: Directives i Funcionalitats Avançades de Plantilles
- Directives Incorporades: classMap, styleMap i ifDefined
- Directives Personalitzades
- Renderitzat Asíncron amb until
- Context Compartit amb @lit/context
Mòdul 8: Integració, Interoperabilitat i Desplegament
- Utilitzar Components Lit en HTML Pla
- Integrar Lit amb React, Vue i Angular
- Renderitzat al Servidor amb @lit-labs/ssr
- Empaquetatge, Publicació i TypeScript
Mòdul 9: Proves i Bones Pràctiques
- Proves Unitàries amb Web Test Runner
- Accessibilitat en Web Components
- Rendiment i Optimització
- Patrons i Antipatrons Comuns
