Da Gatsby ad Astro - Le slot

Nell’articolo precedente abbiamo visto come funziona un componente Astro. Comunque, ci sono ancora alcune cose che non abbiamo trattato; una di queste è come passare degli elementi figlo al nostro componente.

In pratica, questo è il risultato che vogliamo ottenere:

<Component>
    <Child />
    <OtherChild />
</Component>

Questo ci permette di creare componenti molto più versatili e riutilizzabili. Inoltre, è fondamentale per la creazione dei Layout.

Aggiungere questa funzionalità ad un nostro componente è estremamente semplice, grazie ad un elemento speciale fornito da astro: l’elemento <slot />.

Funzionamento di base

Per consentire ad un nostro componente di accettare dei figli, è sufficiente aggiungere il tag <slot /> nel punto in cui vogliamo che compaiano. Pensiamo per esempio ad un componente che crea un testo collassabile, simile a questo:

TitoloContenuto

Potremmo scriverlo più o meno così:

---
// filename: DetailsComponent.astro

const { title } = Astro.props;
---

<details>
    <summary>{title}</summary>
    <slot />
</details>

Tutto qui! Adesso possiamo usare il nostro componente passandogli del contenuto:

<DetailsComponent title="Titolo">
    <b>Contenuto</b>
</DetailsComponent>

Usare più slot assegnando dei nomi

In certi casi, come per la creazione di un componente da usare come layout, è necessario poter inserire dei componenti figli in varie posizioni nel codice HTML.

Astro risponde a questa necessità tramite la possibilità di utilizzare più slot assegnando loro dei nomi. Quando usiamo il nostro componente, possiamo indicare nei figli a quale slot dovranno essere assegnati.

Riprendiamo adesso l’esempio precedente e modifichiamolo in modo da utilizzare uno slot anche per il titolo, oltre che per il contenuto. Daremo alla slot il nome “title”:

---
// filename: DetailsComponent.astro
---
<details>
    <summary><slot name="title" /></summary>
    <slot />
</details>

Ora, per inserire un contenuto nello slot del titolo, aggiungeremo all’elemento l’attributo slot="title"

<DetailsComponent>
    <span slot="title">Titolo inserito nella slot</span>
    <b>Contenuto</b>
</DetailsComponent>

Ed ecco il risultato, praticamente identico a prima:

Titolo inserito nella slotContenuto

Come potete notare, in questo esempio abbiamo utilizzato sia una slot con un nome (“title”) sia una senza nome. Questo ci permette di avere una slot di default, dove andranno a finire tutti gli elementi e i componenti ai quali non viene assegnata esplicitamente una posizione.

Impostare un fallback

Nell’esempio precedente, cosa succede se non passiamo alcun componente alla slot con il nome “title”?

Ecco il risultato:

Contenuto

Hmm… non molto piacevole, vero? Probabilmente sarebbe meglio poter avere un valore di base per quando non ci sono degli elementi in una slot.

Anche Astro la pensa così, motivo per cui ci dà la possibilità di indicare un fallback. Modifichiamo il componente aggiungendo questa funzionalità:

---
// filename: DetailsComponent.astro
---
<details>
    <summary>
        <slot name="title">
            Clicca per scoprire il contenuto
        </slot>
    </summary>
    <slot />
</details>

In questo passaggio, abbiamo modificato l’elemento <slot> facendo in modo che non venga chiuso immediatamente, ma che invece contenga del testo (qualsiasi elemento HTML o componente può essere inserito qui ovviamente) che comparirà quando non ci sono altri elementi nella slot.

Se utilizziamo il componente senza inserire un titolo, come abbiamo fatto prima, ora il risultato è questo:

Clicca per scoprire il contenuto Contenuto

Trasferire una slot al componente superiore

Ora che abbiamo capito come utilizzare le slot, possiamo creare i componenti da usare come layout del nostro sito. Dato che i layout possono essere suddivisi e inseriti uno dentro l’altro, probabilmente decideremo di creare diversi layout e utilizzare il principio della composizione per dare forma al nostro sito.

I layout degli articoli di questo sito sono strutturati più o meno così:

Base → Layout → BlogPostLayout

Il componente Base imposta i tag html, head e body, gestendo anche la parte di SEO e il banner dei cookie. Qui ho anche impostato alcuni stili globali che voglio che siano applicati a tutto il sito. Questo Layout è usato in tutte le pagine, articoli e progetti del sito.

---
// Componente "Base" semplificato
---
<html>
    <head>
        <!-- ... -->
    </head>
    <body>
        <slot />
        <CookieBanner />
    </body>
</html>

Il componente Layout è invece incaricato di gestire l’header e il footer del sito. E’ utilizzato negli articoli e nei progetti, ma, per esempio, non nella Home.

---
// Componente "Layout" semplificato
---
<Base>
    <Header />
    <main>
        <slot />
    </main>
    <Footer />
</Base>

Infine, il componente BlogPostLayout racchiude il contenuto dei miei post. Questo componente gestisce anche la visualizzazione della tabella dei contenuti, degli articoli recenti, delle breadcrumbs e altro ancora. Questo componente è utilizzato solo negli articoli. I progetti utilizzano un componente diverso per il loro layout.

---
// Componente "BlogPostLayout" semplificato
const { frontmatter, headings } = Astro.props;
---
<Layout>
    <Breadcrumbs />
    <main>
        <div class="post-heading">
            <h1>{frontmatter.title}</h1>
        </div>
        <TableOfContents />
        <slot />
        <LatestPosts />
    </main>
</Layout>

In questa circostanza, riscontriamo un problema. I dati necessari al SEO di ogni articolo sono visualizzabili solo dal componente BlogPostLayout, in quanto questo è quello che ho deciso di utilizzare per mostrare la collezione dei miei articoli di blog ed è l’unico ad avere l’accesso a dati come il titolo, il riassunto, la data, ecc… Partendo da questi dati posso quindi creare i tag <meta> necessari, ma non è possibile metterli nell’elemento <head> del sito, in quanto viene gestito dal componente Base.

Per risolvere questo problema, Astro permette di trasferire le slot da un componente a quello superiore. Funziona un po’ come una staffetta: il componente BlogPostLayout passa i tag meta al componente Layout, e quest’ultimo li passa al componente Base.

La sintassi per trasferire una slot è molto semplice. Nel mio layout Base ho questa slot:

<slot name="head" />

Nel componente BlogPostLayout posso aggiungere l’attributo slot="seo" al tag meta:

<meta slot="seo" name="description" content={frontmatter.description} />

Essendo questi due componenti separati dal componente Layout, è necessario che quest’ultimo trasferisca la sua slot “seo” al componente Base. Lo si può fare creando una slot nel componente Layout e aggiungendole sia un nome che l’attributo slot="...". Questo ci permette di redirigere tutti gli elementi associati a quella slot al componente superiore. Nel mio caso, questo è il codice:

<slot name="seo" slot="head" />

Questo semplice codice permette di trasferire tutti i componenti che finirebbero nello slot “seo” del componente Layout allo slot “head” del componente Base

Il prossimo articolo

Nel prossimo articolo andremo ad analizzare un po’ più a fondo il concetto di collezioni e come si usano.

A presto!