Interrogare i contenuti

In questa pagina

EmDash fornisce funzioni di query per recuperare contenuti nelle tue pagine e componenti Astro. Queste funzioni seguono il pattern delle live content collections di Astro, restituendo risultati strutturati con gestione degli errori.

Funzioni di query

FunzioneScopoRestituisce
getEmDashCollectionRecuperare tutte le entry di un tipo di contenuto{ entries, error }
getEmDashEntryRecuperare una singola entry per ID o slug{ entry, error, isPreview }
import { getEmDashCollection, getEmDashEntry } from "emdash";

Ottenere tutte le entry

---
import { getEmDashCollection } from "emdash";

const { entries: posts, error } = await getEmDashCollection("posts");

if (error) {
  console.error("Impossibile caricare i post:", error);
}
---

<ul>
  {posts.map((post) => (
    <li>{post.data.title}</li>
  ))}
</ul>

Filtrare per locale

Quando l’i18n è abilitato, filtra per locale per recuperare contenuti in una lingua specifica:

const { entries: frenchPosts } = await getEmDashCollection("posts", {
	locale: "fr",
	status: "published",
});

const { entries: localizedPosts } = await getEmDashCollection("posts", {
	locale: Astro.currentLocale,
	status: "published",
});

Per entry singole, passa locale come terzo argomento:

const { entry: post } = await getEmDashEntry("posts", "my-post", {
	locale: Astro.currentLocale,
});

Quando locale viene omesso, viene usato per default il locale della richiesta corrente. Se non esiste una traduzione per il locale richiesto, viene seguita la catena di fallback.

Filtrare per stato

const { entries: published } = await getEmDashCollection("posts", {
	status: "published",
});

const { entries: drafts } = await getEmDashCollection("posts", {
	status: "draft",
});

Limitare i risultati

const { entries: recentPosts } = await getEmDashCollection("posts", {
	status: "published",
	limit: 5,
});

Filtrare per tassonomia

const { entries: newsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: "news" },
});

const { entries: jsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { tag: "javascript" },
});

const { entries: featuredNews } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: ["news", "featured"] },
});

Il filtro where usa la logica OR quando vengono forniti più valori per una singola tassonomia.

Gestione degli errori

const { entries: posts, error } = await getEmDashCollection("posts");

if (error) {
	console.error("Impossibile caricare i post:", error);
	return new Response("Errore del server", { status: 500 });
}

Ottenere una singola entry

---
import { getEmDashEntry } from "emdash";
import { PortableText } from "emdash/ui";

const { slug } = Astro.params;
const { entry: post, error } = await getEmDashEntry("posts", slug);

if (error) {
  return new Response("Errore del server", { status: 500 });
}

if (!post) {
  return Astro.redirect("/404");
}
---

<article>
  <h1>{post.data.title}</h1>
  <PortableText value={post.data.content} />
</article>

Tipo di ritorno dell’entry

interface EntryResult<T> {
	entry: ContentEntry<T> | null;
	error?: Error;
	isPreview: boolean;
}

interface ContentEntry<T> {
	id: string;
	data: T;
	edit: EditProxy;
}

Rendere i dati del pannello SEO

Per le collezioni con supports: ["seo"], gli editor possono impostare un titolo SEO, meta description, immagine OG, URL canonico e un toggle “nascondi dai motori di ricerca” (noindex) nel pannello SEO dell’admin. Quei dati vengono forniti come entry.data.seo. Usa getSeoMeta per risolvere i campi del pannello in meta tag pronti per il rendering:

---
import { getEmDashEntry, getSeoMeta } from "emdash";

const { entry, error } = await getEmDashEntry("posts", Astro.params.slug);
if (error) {
  return new Response("Errore del server", { status: 500 });
}
if (!entry) return Astro.redirect("/404");

const seo = getSeoMeta(entry, {
  siteTitle: "Il Mio Sito",
  siteUrl: "https://example.com",
  path: Astro.url.pathname,
});
---

<head>
  <title>{seo.title}</title>
  {seo.description && <meta name="description" content={seo.description} />}
  {seo.ogImage && <meta property="og:image" content={seo.ogImage} />}
  {seo.canonical && <link rel="canonical" href={seo.canonical} />}
  {seo.robots && <meta name="robots" content={seo.robots} />}
</head>

Modalità anteprima

EmDash gestisce l’anteprima automaticamente tramite middleware. Quando un URL contiene un token _preview valido, il middleware lo verifica e configura il contesto della richiesta:

---
import { getEmDashEntry } from "emdash";

const { slug } = Astro.params;
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);

if (error) {
  return new Response("Errore del server", { status: 500 });
}
if (!entry) {
  return Astro.redirect("/404");
}
---

{isPreview && (
  <div class="preview-banner">
    Visualizzazione anteprima. Questo contenuto non è pubblicato.
  </div>
)}

<article>
  <h1>{entry.data.title}</h1>
  <PortableText value={entry.data.content} />
</article>

Editing visuale

Ogni entry restituita include un proxy edit per annotare i tuoi template:

<article {...entry.edit}>
  <h1 {...entry.edit.title}>{entry.data.title}</h1>
  <div {...entry.edit.content}>
    <PortableText value={entry.data.content} />
  </div>
</article>

In modalità modifica, {...entry.edit.title} produce un attributo data-emdash-ref. In produzione, gli spread del proxy non producono output.

Stilizzare blocchi di codice inline

ProprietàScopo
--emdash-inline-code-backgroundSfondo del blocco di codice
--emdash-inline-code-foregroundTesto di codice semplice
--emdash-inline-code-mutedCommenti e testo citato
--emdash-inline-code-keywordParole chiave, letterali, selettori e testo eliminato
--emdash-inline-code-stringStringhe, attributi, simboli e testo aggiunto
--emdash-inline-code-numberNumeri e metadati
--emdash-inline-code-titleTitoli, nomi, tipi e built-in
--emdash-inline-code-borderBordo del selettore lingua
--emdash-inline-code-control-backgroundSfondo del selettore lingua
--emdash-inline-code-control-foregroundTesto e icone del selettore lingua
--emdash-inline-code-focusIndicatore di focus tastiera
:root {
  --emdash-inline-code-background: #f7f7f5;
  --emdash-inline-code-foreground: #24292f;
  --emdash-inline-code-muted: #57606a;
  --emdash-inline-code-keyword: #b8172a;
  --emdash-inline-code-string: #0a3069;
  --emdash-inline-code-number: #0550ae;
  --emdash-inline-code-title: #7545c7;
  --emdash-inline-code-border: #7d8590;
  --emdash-inline-code-control-background: #fff;
  --emdash-inline-code-control-foreground: #24292f;
  --emdash-inline-code-focus: #0550ae;
}

:root.dark {
  --emdash-inline-code-background: #202020;
  --emdash-inline-code-foreground: #f0f3f6;
  --emdash-inline-code-muted: #c9d1d9;
  --emdash-inline-code-keyword: #ffc1bb;
  --emdash-inline-code-string: #b9ddff;
  --emdash-inline-code-number: #a8d5ff;
  --emdash-inline-code-title: #e5ccff;
  --emdash-inline-code-border: #6e7681;
  --emdash-inline-code-control-background: #161b22;
  --emdash-inline-code-control-foreground: #f0f3f6;
  --emdash-inline-code-focus: #a8d5ff;
}

Ordinare i risultati

const { entries: posts } = await getEmDashCollection("posts", { status: "published" });

const sorted = posts.sort(
	(a, b) => (b.data.publishedAt?.getTime() ?? 0) - (a.data.publishedAt?.getTime() ?? 0),
);

Pattern di ordinamento comuni

posts.sort((a, b) => a.data.title.localeCompare(b.data.title));
posts.sort((a, b) => (a.data.order ?? 0) - (b.data.order ?? 0));
posts.sort(() => Math.random() - 0.5);

Tipi TypeScript

npx emdash types
import { getEmDashCollection, getEmDashEntry } from "emdash";
import type { Post } from "../.emdash/types";

const { entries: posts } = await getEmDashCollection<Post>("posts");
const { entry: post } = await getEmDashEntry<Post>("posts", "my-post");

Rendering statico vs. server

Statico (Pre-renderizzato)

---
import { getEmDashCollection, getEmDashEntry } from "emdash";

export async function getStaticPaths() {
  const { entries: posts } = await getEmDashCollection("posts", {
    status: "published",
  });
  return posts.map((post) => ({ params: { slug: post.data.slug } }));
}

const { slug } = Astro.params;
const { entry: post } = await getEmDashEntry("posts", slug);
---

Rendering server

---
export const prerender = false;
import { getEmDashEntry } from "emdash";

const { slug } = Astro.params;
const { entry: post, error } = await getEmDashEntry("posts", slug);

if (error) return new Response("Errore del server", { status: 500 });
if (!post) return new Response(null, { status: 404 });
---

Considerazioni sulle prestazioni

Caching

---
const { entries: posts } = await getEmDashCollection("posts", { status: "published" });
Astro.response.headers.set("Cache-Control", "public, max-age=300");
---

Evitare query ridondanti

---
import { getEmDashCollection } from "emdash";
import PostList from "../components/PostList.astro";
import Sidebar from "../components/Sidebar.astro";

const { entries: posts } = await getEmDashCollection("posts", { status: "published" });
const featured = posts.filter((p) => p.data.featured);
const recent = posts.slice(0, 5);
---

<PostList posts={featured} />
<Sidebar posts={recent} />

Prossimi passi