Nella precedente lezione, abbiamo visto come inserire record nel database. Ora vedremo come recuperare e visualizzare i dati e come aggiornare ed eliminare i record.
Astro e htmx: il recupero dei dati
L'endpoint /list-items
risponde a una richiesta GET attivata dalla ul#todo-list
presente nel file /src/pages/index.astro
. Ogni volta che viene caricata la pagina, la richiesta viene elaborata e l'endpoint restituisce il codice che genera l'elenco dei task. I dati di ogni task vengono visualizzati in una Card.
Creiamo un nuovo file list-items.astro
nella cartella /src/pages
e aggiungiamo al suo interno il seguente codice:
---
import { db, Todos, Categories, eq } from 'astro:db';
import Card from '../components/Card.astro';
const todos = await db.select().from(Todos).innerJoin(Categories, eq(Todos.catId, Categories.id));
---
{
todos.map(({ Todos, Categories }) => (
<Card
id={Todos.id}
title={Categories.category}
body={Todos.task}
done={Todos.done}
/>
))
}
Qui abbiamo selezionato tutti i record della tabella Todos
e utilizzato una INNER JOIN per recuperare la categoria corrispondente dalla tabella Categories
. Abbiamo quindi mappato l'oggetto todos
per generare una Card
per ogni task.
La modifica di un campo
Ora abbiamo bisogno di un endpoint che ci permetta di contrassegnare un task come completo o riportarlo allo stato precedente. In pratica, bisognerà modificare il valore del campo done
di un record della tabella Todos
.
Nella lezione precedente abbiamo inserito nel componente Card
un pulsante Complete
, che attiva una richiesta PATCH
verso l'endpoint /update-item
. L'endpoint /update-item
eseguirà una query UPDATE e restituirà il markup di una nuova Card.
Creiamo il file /src/pages/update-item.astro
e scriviamo il seguente codice:
---
import { db, Todos, Categories, eq, not } from 'astro:db';
import Card from '../components/Card.astro';
let taskId = 0;
let title = '';
let body = '';
let done = false;
if (Astro.request.method === 'PATCH') {
const formData = await Astro.request.formData();
const itemId = Number( formData.get('itemId') );
if (typeof itemId === 'number') {
const updatedRecord = await db.update(Todos)
.set({ done: not(Todos.done) })
.where(eq(Todos.id, itemId))
.returning();
taskId = updatedRecord[0].id;
body = updatedRecord[0].task;
done = updatedRecord[0].done;
const result = await db.select().from(Categories).where(eq(Categories.id, updatedRecord[0].catId));
title = result[0].category;
}
}
---
<Card
id={taskId}
title={title}
body={body}
done={done}
/>
Se il metodo della richiesta è PATCH
e il tipo di itemId
è un numero, allora viene eseguita una query UPDATE per assegnare al campo done
il valore not(Todos.done)
. La successiva query SELECT recupera il titolo della categoria corrispondente dalla tabella Categories
. I valori così ottenuti vengono assegnati agli attributi dell'elemento Card
.
L'eliminazione di un record
L'ultimo step è l'eliminazione di un record. Creiamo un nuovo file /src/pages/delete-item.astro
e scriviamo il seguente codice:
---
import { db, Todos, eq } from 'astro:db';
if (Astro.request.method === 'DELETE') {
const formData = await Astro.request.formData();
const itemId = Number( formData.get('itemId') );
if (typeof itemId === 'number') {
await db.delete(Todos).where(eq(Todos.id, itemId));
}
}
---
Se il metodo è DELETE, viene recuperato l'id
dell'elemento cancellato nel record corrispondente. Non c'è markup da generare, in quanto si dovrà semplicemente eliminare l'elemento corrente.
Un endpoint per la gestione degli errori con Astro e htmx
Nella lezione precedente abbiamo visto che, in caso di errore nella compilazione dei campi del form, viene eseguito un redirect della richiesta verso l'endpoint /error
.
Prima di creare l'endpoint, dobbiamo creare un elemento all'interno del DOM in cui visualizzare il messaggio di errore. Torniamo al file /src/layouts/Layout.astro
e inseriamo una div
subito dopo il tag di apertura dell'elemento body
:
<body>
<div id="error"></div>
<slot />
</body>
Ora, sempre all'interno della cartella /src/pages
, creiamo il file error.astro
e inseriamo questo codice:
<script>console.log("Errore nella compilazione del form");</script>
<div
id="error"
hx-swap-oob="outerHTML"
>
<div class="warning">Error! 😣</div>
</div>
Si noterà che la div
che abbiamo inserito nel file /src/layouts/Layout.astro
ha lo stesso id
della div
del file error.astro
. L'attributo hx-swap-oob
della seconda div
specifica che alcuni contenuti di una risposta devono essere inseriti nel DOM in un punto diverso da quello di destinazione ("Out Of Band"). Ciò vuol dire che, in caso di errore di compilazione del form, la seconda div
andrà a sostituire la prima nel file index.astro
.
Possiamo rendere più evidente il messaggio di errore aggiungendo i seguenti stili nel file Layout.astro
:
.warning {
position: fixed;
top: 0;
left: 0;
width: 100%;
padding: 1em;
font-size: 1.2em;
font-weight: bold;
text-align: center;
background-color: rgb(142, 31, 0);
color: white;
transform: translateY(-100%);
animation-name: errormessage;
animation-duration: 4s;
}
@keyframes errormessage {
0% {transform: translateY(-100%)}
10% {transform: translateY(0%)}
90% {transform: translateY(0%)}
100% {transform: translateY(-100%)}
}
Conclusioni
Il nostro lavoro di sviluppo con Astro DB e htmx si conclude qui, ma c'è ancora qualcosa da fare prima di distribuire l'applicazione.
Per prima cosa, è necessario creare un progetto su Astro Studio seguendo le indicazioni della documentazione online. Una volta autenticati, bisognerà creare un nuovo progetto scegliendo tra le diverse opzioni disponibili.
Una volta collegato il progetto, potremo eseguire diverse operazioni, come collegare l'applicazione al progetto.
Ci fermiamo qui, lasciando ai lettori la configurazione del progetto e il deploy dell'app.
Il codice illustrato in questa guida è disponibile su GitHub. Happy coding!