Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Alpine.js: inizializzazione e ciclo di vita di un componente

Analizziamo il ciclo di vita di un componente in Alpine e come gestire l'inizializzazione e le operazioni asincrone
Analizziamo il ciclo di vita di un componente in Alpine e come gestire l'inizializzazione e le operazioni asincrone
Link copiato negli appunti

Il ciclo di vita di un componente Alpine viene gestito principalmente attraverso alcune direttive e l'evento alpine:init.

Inizializzazione del componente Alpine (x-data)

Quando Alpine rileva un elemento del DOM con la direttiva x-data, dà avvio all'inizializzazione di un componente. Il primo passo è la valutazione dell'espressione contenuta in x-data e la creazione dello stato iniziale del componente. Subito dopo, Alpine esegue il metodo init() se questo è presente in x-data:

<div x-data="{ count: 0, init() { /* ... */ } }">

Quindi, Alpine crea un proxy reattivo dello stato definito in x-data, implementando un sistema reattivo.

Esecuzione di x-init

Dopo l'inizializzazione dello stato, Alpine esegue il codice presente nella direttiva x-init. Questo è il momento ideale per operazioni asincrone (es. fetch API), inizializzazioni complesse e registrazione di eventi globali.

La direttiva x-init non va confusa con il metodo init() che, se definito, viene eseguito automaticamente subito dopo l'inizializzazione del componente. L'esempio che segue mostra la sequenza di esecuzione nella console del browser:

<div
	x-data="{
		init() {
			console.log('Io vengo prima')
		}
	}"
	x-init="console.log('Io vengo dopo')"
>
	Test
</div>

init() viene definito all'interno di un oggetto passato a x-data, mentre la direttiva x-init viene eseguita dopo init() e può essere utilizzata per eseguire codice al di fuori del contesto di x-data.

È preferibile utilizzare x-init per operazioni semplici eseguite una sola volta, per una chiamata a un'API o per separare la logica di inizializzazione. Il metodo init() è preferibile per operazioni complesse da eseguire all'avvio del componente, come operazioni di setup di event listener complessi, fetch di dati iniziali e inizializzazione di librerie esterne.

Dopo un cambiamento di stato: $watch e x-effect

Per "agganciare" un cambiamento di stato, Alpine offre due diverse opzioni: il metodo magico $watch e la direttiva x-effect.

$watch

Il magic method $watch "osserva" una specifica proprietà di un componente e reagisce ai cambiamenti di stato eseguendo una callback:

<div x-data="{ open: false }" x-init="$watch('open', value => console.log(value))">
	<button @click="open = ! open">Toggle</button>
</div>

In questo esempio, ogni volta che il valore di open cambia, $watch esegue la funzione di callback e stampa nella console il nuovo valore. $watch tiene traccia anche del valore precedente della proprietà osservata. Vi si può accedere tramite il secondo argomento della callback:

<div x-data="{ count: 0 }"
	x-init="
		$watch('count', (newVal, oldVal) => {
			console.log(`Count è cambiato da ${oldVal} a ${newVal}`);
		})
	">
	<button @click="count++">Incrementa di 1</button>
	<div>Valore attuale: <span x-text="count"></span></div>
</div>

Con $watch è possibile osservare cambiamenti di proprietà annidate usando la notazione "dot" (deep watching):

<div x-data="{ user: { name: 'Mario', address: { city: 'Roma' } } }"
	x-init="
		$watch('user.address.city', (newVal) => {
			console.log('Città modificata:', newVal);
		})
	">
	<input type="text" x-model="user.address.city" placeholder="Modifica città">
	<div>La città attuale è: <span x-text="user.address.city"></span></div>
</div>

In questi esempi abbiamo utilizzato $watch senza this. Questo perché $watch è un metodo magico disponibile nel contesto del componente. Vedremo più avanti quando è necessario utilizzare this.

x-effect

La direttiva x-effect funziona in modo simile a $watch, ma con alcune differenze significative. Se $watch osserva cambiamenti specifici di una proprietà dello stato in modo esplicito, x-effect rileva implicitamente i cambiamenti di stato di tutte le proprietà o variabili utilizzate nell'espressione.

Un'altra differenza importante è che $watch fornisce sia il vecchio che il nuovo valore della proprietà, mentre x-effect non fornisce il valore precedente.

In sintesi, $watch è ideale per una logica complessa e ogni volta in cui risulta necessario confrontare il vecchio e il nuovo valore di una proprietà, mentre x-effect è più adatto per aggiornare reattivamente elementi del DOM, per effetti visivi, per eseguire chiamate API e in tutti i casi in cui si devono creare effetti che dipendono da più variabili.

Nell'esempio che segue, x-effect aggiorna il titolo della pagina ogni volta che username o pageTitle cambiano.

<div x-data="{
	pageTitle: 'Profilo Utente',
	username: 'Mario'
}">
	<input
		type="text"
		x-model="username"
		placeholder="Inserisci il tuo nome"
	>
	<div x-effect="
		document.title = `${pageTitle}: ${username || 'Anonimo'}`;
		console.log('Titolo aggiornato!');
	"></div>
	<div x-text="`Benvenuto, ${username}!`"></div>
</div>

x-effect

x-effect è ideale per aggiornare reattivamente elementi del DOM

Inizializzazione di Alpine

A volte può essere necessario eseguire del codice una sola volta, subito dopo il caricamento di Alpine ma prima che qualsiasi componente sia inizializzato sulla pagina e che Alpine.js prenda il controllo del DOM.

Definendo un listener sull'evento alpine:init è possibile registrare dati personalizzati, definire un componente globale, registrare direttive o modificare le impostazioni predefinite. Nell'esempio che segue, registriamo il componente riutilizzabile dropdown:

<script>
	document.addEventListener('alpine:init', () => {
		Alpine.data('dropdown', () => ({
			open: false,
			toggle() {
				this.open = !this.open
			}
		}));
	});
</script>
<div x-data="dropdown">
	<button @click="toggle()">Toggle</button>
	<div x-show="open">
		Contenuto del dropdown
	</div>
</div>

Quest'ultimo esempio racchiude i concetti illustrati fino ad ora:

<script>
	document.addEventListener('alpine:init', () => {
		Alpine.data('userProfile', () => ({
			user: {
				name: 'Mario',
				email: 'mario@example.com'
			},
			init() {
				// Osserva 'user.name' con notazione dot
				this.$watch('user.name', (newVal, oldVal) => {
					console.log('Nome cambiato:', oldVal, '→', newVal);
				});
			},
			changeName() {
				this.user.name = 'Luigi';
			}
		}));
	});
</script>
</head>
<body>
	<div x-data="userProfile">
		<input type="text" x-model="user.name">
		<button @click="changeName()">Cambia Nome</button>
		<div x-text="user.name"></div>
	</div>
</body>

  • alpine:init assicura che Alpine sia inizializzato;
  • Alpine.data() crea il componente riutilizzabile userProfile;
  • x-data crea un'istanza del componente userProfile, mentre il metodo init() avvia il watcher;
  • this.$watch imposta il watcher che osserva user.name;
  • il metodo changeName modifica direttamente il valore di user.name;
  • il campo di testo è collegato alla proprietà user.name dalla direttiva x-model;
  • al clic sul pulsante, viene invocato il metodo changeName();
  • infine, la direttiva x-text imposta reattivamente il testo della div.

Si noti che qui abbiamo utilizzato this con $watch perché ci troviamo all'interno di un metodo JavaScript del componente.

Ti consigliamo anche