Alpine.js
Fins ara hem treballat amb este procés:
- definim dades en JavaScript
- seleccionem elements del DOM
- creem elements amb
createElement - els afegim amb
appendChild - reaccionem a events amb
addEventListener - obtenim dades externes amb
fetch
Tot això funciona, però implica escriure bastant codi JavaScript.
Ara farem un canvi important:
deixarem de manipular el DOM manualment i descriurem la interfície directament en el HTML
Per a això utilitzem Alpine.js.
Què és Alpine.js
Alpine.js és una llibreria de JavaScript que permet afegir comportament dinàmic al HTML d’una forma molt més simple.
Amb Alpine podem:
- guardar dades
- mostrar dades en la pàgina
- reaccionar a events
- repetir elements
- treballar amb dades obtingudes amb
fetch
Per tant, Alpine no substituïx JavaScript. El que fa és permetre que moltes coses que abans fèiem amb JavaScript i DOM manual ara es facen d’una manera més directa.
Relació amb el DOM
En el tema anterior, per mostrar una llista fèiem això:
const noms = ["Anna", "Marc", "Pau"];
const llista = document.querySelector("#llista");
noms.forEach(nom => {
const li = document.createElement("li");
li.textContent = nom;
llista.appendChild(li);
});
Ací hem de:
- seleccionar el contenidor
- recórrer les dades
- crear elements
- afegir-los al DOM
Amb Alpine, este procés es simplifica molt.
No deixem de treballar amb el DOM, però deixem de manipular-lo manualment.
Afegir Alpine al projecte
Per utilitzar Alpine, hem de carregar la llibreria.
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
Esta línia s’afegeix normalment dins del <head> o abans de tancar el <body>.
El navegador carregarà Alpine i, a partir d’eixe moment, podrem utilitzar els seus atributs especials en el HTML.
Primer concepte: x-data
El punt de partida d’Alpine és x-data.
x-data definix un espai de dades dins del HTML.
Exemple
<div x-data="{ missatge: 'Hola món' }">
<p x-text="missatge"></p>
</div>
Resultat en la pàgina:
Hola món
Què està passant realment
En este exemple:
x-datacrea un espai de dades- dins tenim una propietat anomenada
missatge x-text="missatge"mostra el seu valor
Açò és important perquè ara les dades i la vista queden molt més connectades.
No fa falta seleccionar el <p> amb querySelector ni modificar-lo amb textContent.
Relació amb el que ja sabem
Abans fèiem això:
<p id="text"></p>
const text = document.querySelector("#text");
text.textContent = "Hola món";
Amb Alpine:
<div x-data="{ missatge: 'Hola món' }">
<p x-text="missatge"></p>
</div>
El resultat és el mateix, però el codi és molt més curt i directe.
x-text
x-text servix per mostrar text dins d’un element.
Exemple
<div x-data="{ nom: 'Anna' }">
<h2 x-text="nom"></h2>
</div>
Resultat:
Anna
Alpine agafa el valor de nom i el col·loca dins de l’element.
Això equival, en la pràctica, a fer un textContent des de JavaScript, però sense escriure el JavaScript manualment.
Canviar dades amb events
En el tema del DOM utilitzàvem addEventListener.
Per exemple:
boto.addEventListener("click", function() {
titol.textContent = "Nou títol";
});
Amb Alpine utilitzem x-on.
Exemple
<div x-data="{ comptador: 0 }">
<button x-on:click="comptador = comptador + 1">Sumar</button>
<p x-text="comptador"></p>
</div>
Cada vegada que fem clic en el botó, el valor de comptador augmenta.
Forma abreujada
També es pot escriure així:
<button @click="comptador = comptador + 1">Sumar</button>
Les dos formes són correctes.
Què està passant
En este exemple:
x-dataconté la dadacomptador@clickexecuta codi quan fem clicx-textmostra el valor actual
No hem seleccionat cap element amb querySelector, no hem creat cap event amb addEventListener i no hem modificat el DOM manualment.
x-model
Fins ara, quan treballàvem amb inputs en JavaScript, fèiem això:
const input = document.querySelector("#nom");
console.log(input.value);
Amb Alpine tenim x-model.
x-model connecta un input amb una dada.
Exemple
<div x-data="{ nom: '' }">
<input type="text" x-model="nom">
<p x-text="nom"></p>
</div>
Quan l’usuari escriu en l’input, el paràgraf s’actualitza automàticament.
Què està passant
nomcomença buitx-model="nom"fa que el valor de l’input i la dadanomestiguen connectatsx-text="nom"mostra el valor actual
Això és una gran diferència respecte al DOM manual.
Abans havíem de:
- seleccionar l’input
- llegir
value - escoltar l’event
input - actualitzar un altre element
Ara tot això queda resolt amb una sola connexió.
x-show
x-show permet mostrar o ocultar un element segons una condició.
Exemple
<div x-data="{ visible: true }">
<button @click="visible = !visible">Mostrar / ocultar</button>
<p x-show="visible">Este text es pot ocultar</p>
</div>
Si visible és true, el paràgraf es mostra. Si visible és false, el paràgraf s’oculta.
Això és útil quan volem controlar la visibilitat d’elements sense eliminar-los del document.
x-bind
x-bind permet assignar atributs de forma dinàmica.
Exemple
<div x-data="{ imatge: 'foto1.jpg' }">
<img x-bind:src="imatge" alt="Imatge">
</div>
Forma abreujada:
<img :src="imatge" alt="Imatge">
Això fa que l’atribut src depenga del valor de la dada imatge.
És l’equivalent a modificar atributs amb JavaScript, però ara la relació queda declarada directament en el HTML.
Repetir elements amb x-for
Este és un dels punts més importants perquè connecta directament amb el que fèiem amb forEach.
Exemple amb dades dins del HTML
<div x-data="{ noms: ['Anna', 'Marc', 'Pau'] }">
<ul>
<template x-for="nom in noms" :key="nom">
<li x-text="nom"></li>
</template>
</ul>
</div>
Resultat:
<li>Anna</li>
<li>Marc</li>
<li>Pau</li>
Què està passant
nomsés un arrayx-forrecorre l’array- per cada element crea un
li x-textmostra el valor
En el tema del DOM havíem de fer això amb:
forEachcreateElementappendChild
Ara el procés queda expressat directament en el HTML.
Per què apareix template
x-for s’aplica habitualment sobre un element <template>.
Això permet a Alpine repetir el contingut de dins tantes vegades com calga.
No és un element que es veja en la pàgina. Servix només com a plantilla interna.
:key en x-for
En els exemples amb x-for apareix:
:key="nom"
La key permet identificar cada element de forma única.
En exemples simples pot semblar un detall menor, però és convenient acostumar-se a posar-la, sobretot quan treballem amb llistes.
Si els elements tenen id, és millor usar l’id.
Treballar amb objectes
També podem guardar objectes dins de x-data.
Exemple
<div x-data="{ alumne: { nom: 'Anna', edat: 17 } }">
<p x-text="alumne.nom"></p>
<p x-text="alumne.edat"></p>
</div>
Resultat:
Anna
17
Ací Alpine està accedint a propietats d’un objecte igual que fèiem en JavaScript.
Exemple més complet
<div x-data="{
alumne: { nom: 'Anna', edat: 17 },
visible: true
}">
<h2 x-text="alumne.nom"></h2>
<p x-text="alumne.edat"></p>
<button @click="visible = !visible">Mostrar / ocultar edat</button>
<p x-show="visible" x-text="alumne.edat"></p>
</div>
En este exemple combinem:
- objectes
- events
- text dinàmic
- mostrar i ocultar
x-init
x-init permet executar codi quan el component es carrega.
Això és especialment important quan volem combinar Alpine amb fetch.
Exemple
<div x-data="{ missatge: 'Carregant...' }" x-init="missatge = 'Contingut preparat'">
<p x-text="missatge"></p>
</div>
Quan el bloc es carrega, s’executa el codi de x-init.
fetch amb Alpine
En el tema anterior utilitzàvem fetch així:
fetch("noms.json")
.then(res => res.json())
.then(noms => {
const llista = document.querySelector("#llista");
noms.forEach(nom => {
const li = document.createElement("li");
li.textContent = nom;
llista.appendChild(li);
});
});
Amb Alpine, la idea és la mateixa, però deixem de manipular el DOM manualment.
Exemple
<div
x-data="{ noms: [] }"
x-init="
fetch('noms.json')
.then(res => res.json())
.then(dades => noms = dades)
"
>
<ul>
<template x-for="nom in noms" :key="nom">
<li x-text="nom"></li>
</template>
</ul>
</div>
Procés complet
x-datacrea una dada inicial:nomscom a array buitx-inits’executa quan el component es carregafetchobté les dades del fitxer- quan arriben, fem
noms = dades - Alpine actualitza automàticament el HTML
Açò és molt important:
ja no creem els elements amb createElement() ni els afegim amb appendChild()
Ara simplement actualitzem les dades i Alpine actualitza la vista.
Relació amb el que hem fet abans
El procés general és el mateix que ja coneixem:
- tenim dades
- les mostrem en la pàgina
La diferència és la manera de fer-ho.
Amb DOM manual
- seleccionem
- creem
- inserim
Amb Alpine
- definim dades
- descrivim el HTML
- Alpine connecta les dos coses
Què estem deixant de fer
Amb Alpine deixem de necessitar, en molts casos:
querySelectortextContentcreateElementappendChildaddEventListener
No perquè deixen d’existir, sinó perquè Alpine resol eixes tasques d’una forma més directa.
Exemple comparatiu
DOM manual
const noms = ["Anna", "Marc", "Pau"];
const llista = document.querySelector("#llista");
noms.forEach(nom => {
const li = document.createElement("li");
li.textContent = nom;
llista.appendChild(li);
});
Alpine
<div x-data="{ noms: ['Anna', 'Marc', 'Pau'] }">
<ul>
<template x-for="nom in noms" :key="nom">
<li x-text="nom"></li>
</template>
</ul>
</div>
El resultat és equivalent, però la versió amb Alpine és més declarativa i més curta.
Resum
- Alpine.js permet afegir comportament dinàmic al HTML
x-datadefinix dadesx-textmostra textx-ono@eventgestiona eventsx-modelconnecta inputs amb dadesx-showmostra o oculta elementsx-bindassigna atributs dinàmicsx-forrepetix elements a partir d’un arrayx-initexecuta codi inicial- Alpine permet combinar dades externes (
fetch) amb una vista que s’actualitza automàticament
Relació amb el següent pas
Amb tot el que hem fet fins ara, el recorregut queda així:
- HTML → estructura
- JavaScript → lògica
- DOM → manipulació manual de la pàgina
- fetch → obtenció de dades externes
- Alpine → connexió directa entre dades i HTML
A partir d’ací, el treball es fa més còmode perquè deixem de centrar-nos en la manipulació manual del DOM i passem a centrar-nos en:
- les dades
- l’estat de la interfície
- la relació entre dades i vista