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:
Titolo
ContenutoPotremmo 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 slot
ContenutoCome 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
ContenutoTrasferire 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!