A la lliçó anterior vas interpolar el teu primer valor dins d'una plantilla html, substituint un text fix per un camp d'instància. Però una interpolació dins de ${} pot fer molt més que inserir un text senzill: pot omplir un atribut HTML, activar o desactivar un atribut booleà, assignar directament una propietat d'un element del DOM, o avaluar qualsevol expressió JavaScript arbitrària. Aquesta lliçó recorre en detall els diferents tipus d'interpolació que admet Lit, amb exemples concrets sobre <task-card>, i estableix les bases sintàctiques que utilitzaràs durant la resta del curs.
Contingut
- Els quatre destins d'una interpolació
- Interpolació de text al contingut d'un node
- Interpolació d'atributs HTML normals
- Interpolació de propietats del DOM amb el prefix
. - Atributs booleans amb el prefix
? - Els esdeveniments amb
@: una menció de passada - Expressions JavaScript arbitràries dins de
${} - Aniuar plantilles
htmldins d'altres - Ampliant
<task-card>amb diversos camps
- Els quatre destins d'una interpolació
Quan escrius ${...} dins d'una plantilla html, Lit necessita decidir com aplicar aquest valor al DOM, i la decisió depèn d'on, sintàcticament, s'ha col·locat la interpolació. Hi ha quatre destins principals, cadascun amb la seva pròpia sintaxi:
| Destí | Sintaxi a la plantilla | Per a què serveix |
|---|---|---|
| Contingut d'un node | <p>${valor}</p> |
Inserir text (o fins i tot una altra plantilla) com a contingut d'un element |
| Atribut HTML | <div id="${valor}"> |
Establir el valor d'un atribut de l'element, com qualsevol atribut HTML |
| Propietat del DOM | <input .value="${valor}"> |
Assignar directament una propietat JavaScript de l'element, sense passar per un atribut |
| Atribut booleà | <button ?disabled="${valor}"> |
Afegir o treure un atribut completament segons si el valor és "vertader" o "fals" |
A aquests quatre s'hi suma un cinquè destí, els gestors d'esdeveniments amb el prefix @ (<button @click="${...}">), que es presenta breument a l'apartat 6 d'aquesta lliçó, però l'estudi en profunditat del qual correspon al mòdul 5, "Esdeveniments i Comunicació entre Components". Els apartats següents desenvolupen cadascun dels quatre destins principals.
- Interpolació de text al contingut d'un node
És el tipus d'interpolació que ja vas fer servir a la lliçó anterior: col·locar ${...} directament entre les etiquetes d'obertura i tancament d'un element.
Lit insereix el valor de this.titulo com a contingut de l'<h3>, tractant-lo sempre com a text pla, mai com a HTML. Això té una conseqüència important per a la seguretat: si this.titulo contingués, per exemple, la cadena '<script>alert(1)</script>', Lit la mostraria literalment com a text visible a pantalla (les etiquetes d'obertura i tancament incloses, com a caràcters), en lloc d'executar-la com a codi. Aquesta és una diferència deliberada respecte a innerHTML, ja comentada a la lliçó anterior, i és el comportament per defecte i recomanat en la gran majoria dels casos.
Aquest destí també admet valors que no són cadenes de text: números, true/false (mostrats literalment com a text "true" o "false"), null i undefined (que no mostren res), arrays de valors (es veurà a la lliçó de renderitzat de llistes), i fins i tot el resultat d'una altra plantilla html aniuada, com s'explica a l'apartat 8.
- Interpolació d'atributs HTML normals
Quan la interpolació apareix dins de les cometes del valor d'un atribut estàndard d'HTML, Lit la tracta com una assignació d'atribut:
Aquest destí equival, aproximadament, a cridar elemento.setAttribute('id', valor). És el destí adequat per a atributs que existeixen a l'HTML com a text: id, class, title, href, src, atributs data-* personalitzats, atributs ARIA d'accessibilitat, etc. Un detall a tenir en compte: si el valor interpolat és undefined, Lit elimina l'atribut completament en lloc d'establir-lo amb el text "undefined"; aquest comportament resulta útil per a atributs opcionals, encara que el mòdul 7 presenta una directiva específica (ifDefined) pensada precisament per expressar aquesta intenció de manera més explícita.
- Interpolació de propietats del DOM amb el prefix
.
.De vegades no interessa establir un atribut HTML (que sempre és text), sinó assignar directament una propietat de JavaScript de l'element del DOM, que pot contenir qualsevol tipus de valor: un objecte, un array, un booleà natiu, una funció. Per a això, Lit ofereix la sintaxi amb un punt davant del nom:
Aquí, .value="${this.textoBusqueda}" no crea ni modifica cap atribut value a l'HTML; en lloc d'això, executa directament elemento.value = this.textoBusqueda, assignant la propietat JavaScript de l'element <input>. La diferència entre atribut i propietat pot semblar subtil, però és important: molts elements natius del navegador (com <input>) mantenen el seu atribut HTML original (el que es va escriure al marcat) separat de la seva propietat JavaScript actual (el valor que l'usuari ha escrit al camp), i en molts casos interessa treballar amb la propietat, no amb l'atribut.
Com a referència ràpida per decidir quin utilitzar:
| Situació | Destí recomanat |
|---|---|
| El valor és sempre una cadena de text simple i té sentit que aparegui a l'HTML | Atribut (attr="${valor}") |
| El valor és un objecte, un array, o qualsevol tipus de dada que no sigui text | Propietat (.prop="${valor}") |
Es treballa amb una propietat específica del DOM que no té atribut HTML equivalent exacte (com .value en formularis avançats) |
Propietat (.prop="${valor}") |
Aquest mateix mecanisme, el de passar propietats (no només atributs) a un element, és també la base de com un component Lit pare passarà dades complexes (objectes, arrays) a un component Lit fill; es reprendrà en detall al mòdul 5 en parlar de comunicació entre components.
- Atributs booleans amb el prefix
?
?Alguns atributs d'HTML són "booleans" en un sentit especial: la seva sola presència a l'etiqueta activa el comportament, independentment del text que se'ls assigni. disabled, checked, hidden o required són exemples habituals. Escriure disabled="false" en HTML pur, de fet, no desactiva el botó: l'atribut continua present, així que el navegador l'interpreta igualment com a deshabilitat.
Per gestionar correctament aquest tipus d'atributs, Lit ofereix la sintaxi amb el prefix ?:
Amb ?disabled="${this.estaBloqueado}", Lit afegeix l'atribut disabled a l'element només si this.estaBloqueado és un valor "vertader" (true, o qualsevol valor que JavaScript tracti com a vertader), i l'elimina completament de l'element si és "fals" (false, null, undefined, 0, cadena buida...). Això reprodueix exactament la semàntica real dels atributs booleans d'HTML: el que importa no és el text de l'atribut, sinó si està present o no.
- Els esdeveniments amb
@: una menció de passada
@: una menció de passadaExisteix un cinquè tipus d'interpolació, amb el prefix @, pensat per associar gestors d'esdeveniments del DOM a un element:
Aquesta sintaxi equival, de forma simplificada, a elemento.addEventListener('click', this.manejarClic). Es menciona aquí únicament perquè reconeguis la sintaxi si la veus en exemples o en la documentació oficial de Lit durant aquest mòdul; el seu estudi complet —incloent com definir correctament el mètode gestor, el problema del valor de this dins d'ell, i com despatxar esdeveniments personalitzats entre components— és el contingut íntegre del mòdul 5, "Esdeveniments i Comunicació entre Components". De moment, cap de les plantilles d'aquest mòdul farà servir encara gestors d'esdeveniments.
- Expressions JavaScript arbitràries dins de
${}
${}Un aspecte que convé interioritzar com abans millor: dins de ${} no només es poden col·locar noms de variables o propietats soltes (${this.titulo}); es pot col·locar qualsevol expressió vàlida de JavaScript que produeixi un valor. Això inclou operacions aritmètiques, concatenació de text, crides a mètodes, operadors ternaris, i qualsevol combinació d'ells:
render() {
return html`
<article>
<h3>${this.titulo.toUpperCase()}</h3>
<p>Prioridad: ${this.prioridad + 1} de 5</p>
<p>${this.completada ? 'Tarea finalizada' : 'Tarea pendiente'}</p>
</article>
`;
}En aquest exemple, ${this.titulo.toUpperCase()} crida el mètode toUpperCase() de la cadena abans d'interpolar el resultat; ${this.prioridad + 1} fa una operació aritmètica; i ${this.completada ? 'Tarea finalizada' : 'Tarea pendiente'} utilitza un operador ternari per triar entre dos textos segons una condició. Aquest darrer patró, el de l'operador ternari dins d'una interpolació, és tan habitual a Lit que la següent lliçó, "Renderizado Condicional", el desenvolupa en profunditat com a tècnica principal per mostrar o amagar contingut.
L'única cosa que no es pot fer dins de ${} és escriure sentències completes de JavaScript (un if amb claus, un bucle for, una declaració const): només s'admeten expressions, és a dir, codi que es redueix a un únic valor. Quan cal lògica més elaborada que una simple expressió, la tècnica habitual és extreure-la a un mètode o funció auxiliar de la classe, i cridar aquest mètode des de dins de ${}, com es fa amb ${this.titulo.toUpperCase()} a l'exemple anterior.
- Aniuar plantilles
html dins d'altres
html dins d'altresCom s'ha apuntat a l'apartat 2, el resultat d'una crida a html (un objecte TemplateResult) és un valor perfectament vàlid per interpolar dins d'una altra plantilla html. Això permet dividir una plantilla gran en fragments més petits i combinar-los:
render() {
const cabecera = html`<h3>${this.titulo}</h3>`;
return html`
<article>
${cabecera}
<p>Estado: ${this.estado}</p>
</article>
`;
}Aquí, cabecera és en si mateixa el resultat d'una crida a html, i s'interpola dins de la plantilla principal exactament igual que s'interpolaria qualsevol altre valor. Lit reconeix que es tracta d'una altra plantilla (no de text pla) i la insereix com a HTML real, no com a text escapat. Aquesta capacitat d'aniuar plantilles és la base de dues tècniques que s'estudiaran a les properes lliçons: extreure fragments condicionals a funcions auxiliars (lliçó de renderitzat condicional) i generar una plantilla per cada element d'un array (lliçó de renderitzat de llistes).
- Ampliant
<task-card> amb diversos camps
<task-card> amb diversos campsAmb tots els tipus d'interpolació ja vistos, és el moment d'ampliar <task-card> per mostrar diversos camps de la tasca alhora: títol, estat i prioritat. Recorda, com es va explicar a la lliçó anterior, que aquests continuen sent camps d'instància simples, encara no propietats reactives.
import { LitElement, html } from 'lit';
class TaskCard extends LitElement {
constructor() {
super();
this.titulo = 'Preparar la demo del sprint';
this.estado = 'En curso';
this.prioridad = 2;
this.urgente = false;
}
render() {
return html`
<article ?data-urgente="${this.urgente}">
<h3>${this.titulo}</h3>
<p>Estado: ${this.estado}</p>
<p>Prioridad: ${this.prioridad} de 5</p>
<p>${this.urgente ? '⚠ Requiere atención inmediata' : 'Sin urgencia especial'}</p>
</article>
`;
}
}
customElements.define('task-card', TaskCard);Repassem les novetats:
${this.titulo},${this.estado}i${this.prioridad} de 5són interpolacions de text senzilles, com ja s'ha vist a la lliçó anterior; la de la prioritat combina el valor interpolat amb text literal addicional dins del mateix node.?data-urgente="${this.urgente}"utilitza la sintaxi d'atribut booleà vista a l'apartat 5: l'atributdata-urgentenomés apareixerà a l'<article>quanthis.urgentesiguitrue. Això és útil, per exemple, perquè uns estils CSS (que es veuran al mòdul 4) puguin aplicar un aspecte diferent a les targetes urgents basant-se en la presència d'aquest atribut.- L'última línia utilitza un operador ternari, com es va explicar a l'apartat 7, per mostrar un text diferent segons el valor de
this.urgente.
En recarregar la pàgina, la targeta mostrarà ara diverses dades combinades. Igual que a la lliçó anterior, si modifiques aquests camps des de la consola del navegador, la pantalla no s'actualitzarà automàticament: aquesta peça continua pertanyent al mòdul 3.
Errors Comuns i Consells
- Utilitzar
.propquan bastaria un atribut normal: si el valor és sempre text simple, no hi ha necessitat d'utilitzar la sintaxi amb punt; l'atribut normal (attr="${valor}") és més llegible i suficient en la majoria dels casos. - Utilitzar un atribut normal per a un booleà: escriure
disabled="${this.estaBloqueado}"(sense el?) no produeix el resultat esperat, perquè Lit convertiria el valor a text i l'assignaria com a atribut amb aquest text, en lloc d'afegir o treure l'atribut segons la seva condició. Per a atributs booleans reals, sempre?attr. - Intentar ficar una sentència completa dins de
${}: codi com${if (this.urgente) { return 'Sí'; }}no és vàlid, perquè${}només admet expressions, no sentències. L'alternativa correcta és un operador ternari o extreure la lògica a un mètode a part. - Oblidar que el contingut de text s'escapa automàticament: si necessites mostrar contingut HTML real generat dinàmicament (no text simple), interpolar directament no n'hi ha prou; existeix un mecanisme específic per a aquest cas concret (la directiva
unsafeHTML) que s'estudiarà, amb els seus riscos de seguretat, en mòduls posteriors. Per defecte, i en el 99% dels casos, la interpolació de text és l'opció correcta i més segura.
Exercicis
- Afegeix a
<task-card>un nou camp d'instànciathis.etiqueta(per exemple,'Backend') i interpola'l com el valor de l'atributdata-etiquetade l'<article>. - Afegeix un camp
this.progreso(un número de 0 a 100) i mostra'l interpolat dins d'un paràgraf juntament amb el símbol de percentatge, per exemple: "Progreso: 40%". - Modifica la interpolació booleana de
this.urgentede l'apartat 9 perquè, en lloc d'un atributdata-urgente, controli l'atribut booleà realhiddend'un petit<span>amb el text "URGENTE" (és a dir, que l'<span>només aparegui quan la tasca sigui urgent).
Solucions
render() {
return html`
<article>
...
<span ?hidden="${!this.urgente}">URGENTE</span>
</article>
`;
}Fixa't en l'ús de la negació (!this.urgente): com que hidden amaga l'element quan està present, cal afegir l'atribut precisament quan la tasca no és urgent, d'aquí la negació del valor original.
Conclusió
En aquesta lliçó has recorregut els diferents destins que pot tenir una interpolació dins d'una plantilla html: contingut de text, atributs HTML normals, propietats del DOM amb el prefix ., i atributs booleans amb el prefix ?, a més d'una menció de passada als gestors d'esdeveniments amb @ que es detallaran al mòdul 5. També has vist que dins de ${} hi cap qualsevol expressió JavaScript, no només noms de variables solts, i que les plantilles html es poden aniuar unes dins d'altres. Amb tot això, <task-card> ara combina diversos camps interpolats: títol, estat, prioritat i un indicador d'urgència.
A la següent lliçó, "Renderizado Condicional", et centraràs en una de les expressions que ja has començat a utilitzar de passada —l'operador ternari dins de ${}— i la desenvoluparàs en profunditat com a tècnica principal per mostrar contingut diferent segons condicions, aplicant-la a una insígnia d'estat a <task-card> que canviarà segons la tasca estigui pendent, en progrés o feta.
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
