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 è 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 riutilizzabileuserProfile
;x-data
crea un'istanza del componenteuserProfile
, mentre il metodoinit()
avvia il watcher;this.$watch
imposta il watcher che osservauser.name
;- il metodo
changeName
modifica direttamente il valore diuser.name
; - il campo di testo è collegato alla proprietà
user.name
dalla direttivax-model
; - al clic sul pulsante, viene invocato il metodo
changeName()
; - infine, la direttiva
x-text
imposta reattivamente il testo delladiv
.
Si noti che qui abbiamo utilizzato this
con $watch
perché ci troviamo all'interno di un metodo JavaScript del componente.