Nella lezione precedente abbiamo creato la pagina di un blog Astro prelevando i contenuti da un sito WordPress. Torniamo per un attimo al codice del file index.astro
per analizzare l'attributo href
dell'elemento a
:
<a href={`/blog/${post.slug}/`}>
<img src={typeof post._embedded['wp:featuredmedia'] !== "undefined" ? post._embedded['wp:featuredmedia']['0'].media_details.sizes.large.source_url : 'https://images.pexels.com/photos/270404/pexels-photo-270404.jpeg'} alt="" />
<h4 class="title">{post.title.rendered}</h4>
<FormattedDate date={post.date} />
</a>
Routing statico e routing dinamico
Di default, i file con estensione .astro
, .html
, .md
, .mdx
, .js
e .ts
presenti nella cartella src/pages
diventano automaticamente pagine del sito. Il percorso di ogni pagina corrisponde al percorso e al nome del file all'interno della cartella. In questo caso parliamo di routing statico.
Quando il sito si compone di decine o centinaia di pagine, è poco pratico affidarsi al routing statico. Astro permette di specificare parametri di percorso dinamici nel nome del file per generare più pagine da uno stesso file, ad esempio [param].astro
.
In modalità statica, le pagine sono generate al momento della build. Quindi è necessario predeterminare un elenco di valori corrispondenti per il parametro che costituisce il nome del file. Ciò avviene esportando la funzione getStaticPaths()
, che dovrà restituire i diversi percorsi da generare.
Ad esempio, [author].astro
definisce il parametro author
. Nell'esempio che segue, vediamo come utilizzare getStaticPaths()
nel file [author].astro
in modo che restituisca un array di oggetti che contengano il parametro author
:
---
export function getStaticPaths() {
return [
{params: {author: 'carlo'}},
{params: {author: 'marco'}},
{params: {author: 'anna'}},
];
}
const { author } = Astro.params;
---
<div>Pagina di {author}!</div>
Il file [slug].astro
Ora torniamo al nostro progetto. Creiamo un file [slug].astro
all'interno della cartella src/pages/blog
e scriviamo il seguente codice:
---
import BlogPost from '../../layouts/BlogPost.astro';
import FormattedDate from '../../components/FormattedDate.astro';
const { slug } = Astro.params;
let res = await fetch(`https://developer.wordpress.org/news/wp-json/wp/v2/posts?slug=${slug}&_embed`);
let [post] = await res.json();
export async function getStaticPaths() {
let data = await fetch("https://developer.wordpress.org/news/wp-json/wp/v2/posts?per_page=21");
let posts = await data.json();
return posts.map((post) => ({
params: { slug: post.slug },
props: { post: post },
}));
}
---
Abbiamo importato il layout BlogPost
e il componente FormattedDate
. Abbiamo dichiarato la proprietà slug
, recuperato il post
corrente grazie alla Rest API di WordPress. Infine, abbiamo esportato la funzione getStaticPaths()
. All'interno di essa abbiamo prima recuperato 21 articoli del blog WordPress, quindi mappato ogni post al corrispondente slug.
Se si dovesse aver bisogno di inserire nell'URL un percorso di file, basterà aggiungere il parametro rest nel nome del file .astro
(ad esempio [...slug].astro
).
Uso dei dati nel template del componente
Vediamo ora come utilizzare i dati nel template del componente:
<BlogPost title={post.title.rendered}>
<article>
<div class="hero-image">
<img width={1020} height={510} src={typeof post._embedded['wp:featuredmedia'] !== "undefined" ? post._embedded['wp:featuredmedia']['0'].media_details.sizes.large.source_url : 'https://images.pexels.com/photos/270404/pexels-photo-270404.jpeg'} alt="" />
</div>
<div class="prose">
<div class="title">
<h1 set:html={post.title.rendered} />
<FormattedDate date={post.date} />
</div>
<hr />
<slot />
</div>
<Fragment set:html={post.content.rendered} />
</article>
</BlogPost>
Rispetto al codice illustrato nella precedente lezione, qui abbiamo fatto ricorso ad alcune utility di Astro.
set:html
è una direttiva Astro che permette di inserire una stringa HTML in un elemento. Il tag<h1>
utilizzato nel codice qui sopra è equivalente a<h1>{post.title.rendered}</h1>
.- L'elemento
Fragment
è utilizzato per evitare di racchiudere il valore diset:html
in un container, ad esempio un elementodiv
. È utile in questo caso in quanto il contenuto del post include la struttura HTML generata da WordPress.
Il file BlogPost.astro
Ora creiamo il file BlogPost.astro
nella cartella src/layouts
e scriviamo il seguente codice:
---
import BaseHead from '../components/BaseHead.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
import { SITE_TITLE, SITE_DESCRIPTION } from '../consts';
---
<html lang="en">
<head>
<BaseHead title={SITE_TITLE} description={SITE_DESCRIPTION} />
<style>...</style>
</head>
<body>
<Header />
<main>
<slot />
</main>
<Footer />
</body>
</html>
Archivi WordPress e paginazione dei risultati in Astro
Quando il numero di articoli cresce, può essere opportuno aggiungere al blog un sistema di paginazione. Possiamo gestire tutto con diverse chiamate all'API di WordPress. O lasciare che sia Astro a gestire la paginazione.
Astro, infatti, dispone nativamente della funzione paginate()
. Questa genera automaticamente l'array restituito da getStaticPaths()
con un URL per ogni pagina della collezione. In questo modo possiamo creare un archivio di post suddiviso in pagine. Creiamo, quindi, una pagina [page].astro
in una nuova cartella src/pages/archive
e scriviamo il seguente codice JavaScript:
---
import PostPreview from '../../layouts/PostPreview.astro';
import FormattedDate from '../../components/FormattedDate.astro';
export async function getStaticPaths({ paginate }) {
const res = await fetch("https://developer.wordpress.org/news/wp-json/wp/v2/posts?per_page=21&_fields=id,slug,date,title,excerpt,content,link,_links&_embed=author,wp:featuredmedia,wp:term");
const archivePages = await res.json();
return paginate(archivePages, { pageSize: 7 });
}
type Props = any;
const { page } = Astro.props;
---
L'URL trasmessa alla Rest API rimane la stessa del blog. Il risultato in formato JSON viene passato come primo argomento alla funzione paginate
. Il secondo argomento è un oggetto i cui elementi stabiliscono le proprietà della paginazione. Qui pageSize
stabilisce il numero di elementi di ogni pagina. Nell'URL, il parametro page
sarà convertito nel numero di pagina.
Il template del file [page].astro
Passiamo ora al codice del template del file [page].astro
.
<PostPreview tytle="Blog">
<section>
<h1>Archives</h1>
<h3>Page {page.currentPage}</h3>
<ul>
{
page.data.map((post) => (
<li>
<a href={`/blog/${post.slug}/`}>
<img src={typeof post._embedded['wp:featuredmedia'] !== "undefined" ? post._embedded['wp:featuredmedia']['0'].media_details.sizes.large.source_url : 'https://images.pexels.com/photos/270404/pexels-photo-270404.jpeg'} alt="" />
<h4 class="title">{post.title.rendered}</h4>
<FormattedDate date={post.date} />
</a>
</li>
))
}
</ul>
</section>
<hr />
<div class="pagination">
{page.url.prev ? <a href={page.url.prev}>Previous</a> : null}
{page.url.next ? <a href={page.url.next}>Next</a> : null}
</div>
</PostPreview>
Con la paginazione, viene passata ad ogni pagina renderizzata una proprietà page
che rappresenta una singola pagina di dati. Si tratta dei dati che sono stati suddivisi in pagine (page.data
) e dei metadati corrispondenti. Tra di essi page.url
, page.start
, page.end
, page.total
, page.currentPage
, ecc.
Nel codice qui sopra abbiamo utilizzato page.currentPage
per il numero di pagina corrente, e page.url.prev
e page.url.next
per i link di navigazione. Le pagine di archivio così generate avranno lo stesso aspetto della pagina del blog. Con la sola differenza del numero di post per pagina e dei link di navigazione in basso.
La build e la distribuzione di un sito Astro
Il comando npm run build
avvia il processo di build del progetto Astro. Questo genera una cartella dist
all'interno della quale vengono archiviati i file HTML del sito.
La distribuzione sul servizio di hosting sarà diversa a seconda dell'host. Per questo, rinviamo alla documentazione online di Astro. Dove vengono riportate guide specifiche per ogni host. Il codice illustrato in questa guida è disponibile su GitHub.