Alpine.js non ha un sistema nativo per il recupero di dati da un'origine remota, ma si integra bene con le API di JavaScript e con librerie esterne. Quando si recuperano dati da un'origine remota e li si assegnano ad una proprietà di x-data
, Alpine aggiorna reattivamente il DOM. In genere il recupero dei dati avviene in fase di inizializzazione ma può avvenire anche successivamente, ad esempio al verificarsi di un evento clic.
Per il recupero dei dati, si può far ricorso alla fetch API nativa di Javascript o a una libreria di terze parti come axios. Vediamo un primo esempio in cui mettiamo insieme alcuni concetti acquisiti nelle lezioni precedenti.
Fetch asincrono di dati con x-init
di Alpine.js
Vediamo un primo semplice esempio di recupero di dati da un'origine remota con x-init
e la fetch API. I dati ci vengono forniti dalla Star Wars API:
<div
x-data="{
planets: [],
loading: true,
error: null
}"
x-init="
fetch('https://swapi.dev/api/planets')
.then(response => response.json())
.then(data => {
planets = data.results;
loading = false;
})
.catch(error => {
console.error('Errore:', error);
loading = false;
error = 'Errore nel caricamento dei dati';
})
">
<template x-if="loading">
<p>Caricamento...</p>
</template>
<template x-if="error">
<p x-text="error"></p>
</template>
<template x-if="!loading && !error">
<ul>
<template x-for="planet in planets" :key="planet.name">
<li x-text="planet.name"></li>
</template>
</ul>
</template>
</div>
Analizziamo il codice.
- All'avvio del componente (
x-init
),fetch
interroga l'origine dati remota; response => response.json()
converte la risposta in JSON;- la successiva callback assegna i risultati (
data.results
) alla proprietàplanets
e impostaloading
sufalse
, a indicare che il caricamento è terminato. Si noti che non è necessario utilizzare.this
con le proprietàplanet
eloading
in quanto il codice è scritto direttamente nell'attributox-init
come sequenza di istruzioni, non come un metodo separato. In questo contesto, Alpine.js interpreta automaticamente le variabili come appartenenti allo scope dix-data
. - In caso di errore (
.catch()
), viene inviato un messaggio alla console del browser e impostata una proprietàerror
. - All'interno del componente, abbiamo diversi elementi
template
. I primi sono utilizzati con la direttivax-if
per visualizzare condizionalmente il contenuto del blocco. Un elementotemplate
è quindi utilizzato conx-for
e genera un elemento di lista per ogni elemento dell'arrayplanets
.
Questo codice dovrebbe generare il seguente elenco:

Un elenco di risultati prelevati da un'origine remota
Fetch asincrono di dati con Alpine: un esempio avanzato
In questo secondo esempio, consentiremo all'utente di selezionare il tipo di dati desiderato utilizzando tutti gli attributi della SWAPI:
{
"films": "https://swapi.dev/api/films/",
"people": "https://swapi.dev/api/people/",
"planets": "https://swapi.dev/api/planets/",
"species": "https://swapi.dev/api/species/",
"starships": "https://swapi.dev/api/starships/",
"vehicles": "https://swapi.dev/api/vehicles/"
}
Ecco il codice completo da copiare e incollare nel proprio editor:
<html>
<head>
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body>
<div x-data="{
items: [], // array che memorizza gli elementi della risposta
loading: false,
selectedType: 'planets', // valore predefinito
async fetchData() {
try {
this.loading = true;
this.items = []; // resetta il valore precedente
const response = await fetch(`https://swapi.dev/api/${this.selectedType}/`);
this.items = (await response.json()).results; // aggiorna con il nuovo set di dati
this.loading = false;
} catch (error) {
console.error('Errore:', error);
this.loading = false;
}
}
}" x-init="fetchData()">
<!-- Selezione del tipo di dati -->
<select x-model="selectedType" @change="fetchData()">
<option value="films">Films</option>
<option value="people">People</option>
<option value="planets" selected>Planets</option>
<option value="species">Species</option>
<option value="starships">Starships</option>
<option value="vehicles">Vehicles</option>
</select>
<template x-if="loading">
<p>Caricamento...</p>
</template>
<template x-if="!loading && items.length > 0">
<ul>
<template x-for="item in items" :key="item.url">
<li x-text="item.name || item.title"></li>
</template>
</ul>
</template>
<template x-if="!loading && items.length === 0">
<p>Nessun dato disponibile</p>
</template>
<button @click="fetchData()">Ricarica</button>
</div>
</body>
</html>
In questo esempio non carichiamo i dati solo all'inizializzazione del componente ma ogni volta che l'utente interagisce con l'applicazione. Ha senso, quindi, spostare la logica all'interno di x-data
e dichiarare la costante response
in questo modo:
const response = await fetch(`https://swapi.dev/api/${this.selectedType}/`);
Il menu select
è collegato alla proprietà selectedType
e invoca fetchData()
ogni volta che l'utente seleziona un elemento diverso del menu:
<select x-model="selectedType" @change="fetchData()">
<option value="films">Films</option>
<option value="people">People</option>
<option value="planets" selected>Planets</option>
<option value="species">Species</option>
<option value="starships">Starships</option>
<option value="vehicles">Vehicles</option>
</select>
Cambiando la selezione, si aggiorna il valore di selectedType
e viene eseguito nuovamente il metodo fetchData()
con la nuova richiesta.
Il diverso testo visualizzato negli elementi di lista (<li x-text="item.name || item.title"></li>
) dipende dal fatto che alcune risorse dell'API usano la proprietà name
, altre la proprietà title
.

Un elenco di risultati prelevati a seguito di input dell'utente
Un sistema di navigazione tra i dati
La SWAPI fornisce 10 risultati per ogni richiesta. Possiamo perfezionare il componente aggiungendo un sistema di navigazione tra i dati:
<div x-data="{
items: [],
loading: false,
selectedType: 'planets',
currentPage: 1, // Pagina corrente
nextUrl: null, // URL della pagina successiva
prevUrl: null, // URL della pagina precedente
async fetchData(url = `https://swapi.dev/api/${this.selectedType}/`) {
this.loading = true;
this.items = [];
const response = await fetch(url);
const data = await response.json();
this.items = data.results;
this.nextUrl = data.next;
this.prevUrl = data.previous;
this.loading = false;
}
}" x-init="fetchData()">
<!-- Selezione del tipo di dati -->
<div>
<label for="type-select">Seleziona tipo:</label>
<select id="type-select" x-model="selectedType" @change="currentPage = 1; fetchData()">
<option value="films">Films</option>
<option value="people">People</option>
<option value="planets" selected>Planets</option>
<option value="species">Species</option>
<option value="starships">Starships</option>
<option value="vehicles">Vehicles</option>
</select>
</div>
<!-- Stato di caricamento -->
<template x-if="loading">
<p>Caricamento...</p>
</template>
<!-- Lista dei risultati -->
<template x-if="!loading && items.length > 0">
<div>
<ul>
<template x-for="item in items" :key="item.url">
<li x-text="item.name || item.title"></li>
</template>
</ul>
<!-- Pulsanti per la paginazione -->
<div>
<button @click="fetchData(prevUrl); currentPage--" :disabled="!prevUrl">Precedente</button>
<span x-text="`Pagina ${currentPage}`"></span>
<button @click="fetchData(nextUrl); currentPage++" :disabled="!nextUrl">Successivo</button>
</div>
</div>
</template>
<template x-if="!loading && items.length === 0">
<p>Nessun risultato trovato</p>
</template>
<button @click="fetchData(`https://swapi.dev/api/${selectedType}/?page=${currentPage}`)">Ricarica</button>
</div>
Abbiamo aggiunto currentPage
, nextUrl
e prevUrl
al nostro set di dati.
Nel codice HTML abbiamo, quindi, aggiunto due pulsanti di navigazione e uno span
:
<div>
<button @click="fetchData(prevUrl)" :disabled="!prevUrl">Precedente</button>
<span x-text="`Pagina ${currentPage}`"></span>
<button @click="fetchData(nextUrl); currentPage++" :disabled="!nextUrl">Successivo</button>
</div>
Infine, abbiamo aggiunto page=${currentPage}
all'URL passata a fetchData
al clic sul pulsante "Ricarica" in modo da rimanere sulla stessa pagina in caso di ricarica manuale da parte dell'utente.

Il componente completo con un sistema di navigazione
Conclusioni
L'applicazione è completa, ma per i nostri lettori potrebbe essere un utile esercizio migliorarla
- aggiungendo altri dati al set della risposta;
- salvando i dati della risposta nel
localStorage
; - utilizzando
x-show
per mostrare o nascondere i dati aggiuntivi di ogni elemento.