Seiten lesen
Wie eine Seite Inhalte liest. useGinko, useGinkoEntry, useGinkoToday, GinkoMarkdown, formatMoney, Fallback, Fehler und eine Referenz aller Funktionen.
useGinko
useGinko(baustein) liest einen Baustein. Der Rückgabewert ist ein ComputedRef. In <script> greifst du mit .value zu, im Template nicht.
- Für ein Einzelstück liefert er
{ data, fallback }.dataenthält die Felder. - Für eine Sammlung liefert er
{ items }. Jedes Element hatid,dataundfallback. Ein Element eines Bausteins mitroutehat dazuurl, seine Adresse in der aktuellen Sprache (nutze sie für Links), undurls, seine Adresse in jeder Sprache, in die es übersetzt ist (für einen Sprachumschalter). Steht ein Eintrag mituntranslated: 'fallback'in einer Sprache ohne Übersetzung in der Liste, isturldie Seite in der Hauptsprache.updatedAtist die Zeit der letzten Änderung (ISO 8601, UTC), wenn sie bekannt ist.
<script setup lang="ts">
import { formatMoney } from '@lupinum/ginko'
const menu = useGinko('menu')
const { language } = useI18n()
</script>
<template>
<ul>
<li v-for="item in menu.items" :key="item.id">
{{ item.data.name }}: {{ formatMoney(item.data.price, language) }}
</li>
</ul>
</template>useI18n() gehört zu ginko-i18n. Es liefert das Sprach-Tag der aktuellen Sprache (etwa de-AT) als language. useGinko ist automatisch importiert. formatMoney nicht: importiere es aus @lupinum/ginko.
Die Typen kommen aus deiner ginko.config.ts. item.data.price ist dort ein f.money-Feld, also ein { amount, currency }. Ein freiwilliges Feld kann undefined sein.
useGinko gibt bei einer Sammlung nur sichtbare Einträge zurück, in der Reihenfolge des Bausteins. Die Regel steht in „Bausteine“, Abschnitt „Sichtbarkeit“.
Bekommt eine Komponente einen Eintrag als Prop, nimm den Typ GinkoItem mit dem Namen des Bausteins. Er wird automatisch importiert:
<script setup lang="ts">
defineProps<{ dish: GinkoItem<'menu'> }>()
</script>useGinkoEntry
useGinkoEntry(baustein, slug) liest einen Eintrag einer Sammlung mit route. Du gibst den Slug aus der Adresse als Funktion mit. So folgt der Eintrag der Route, wenn Nuxt die Seite für einen anderen Eintrag wiederverwendet. Die Datei liegt zum Beispiel unter app/pages/veranstaltungen/[slug].vue:
<script setup lang="ts">
definePageMeta({ i18n: { paths: { en: '/events/:slug()' } } })
const route = useRoute()
const event = useGinkoEntry('events', () => route.params.slug)
</script>
<template>
<article>
<h1>{{ event.data.title }}</h1>
<GinkoMarkdown :value="event.data.description" />
</article>
</template>useGinkoEntry baut den Pfad aus der Route des Bausteins und dem Slug. Eine Slug-Liste (von einer Catch-all-Seite wie [...slug].vue) wird mit / verbunden. Dann sucht es den sichtbaren Eintrag, dessen Adresse in der aktuellen Sprache genau dieser Pfad ist. Findet es keinen, antwortet die Seite mit einem echten 404. Das passiert bei einem falschen oder fehlenden Slug, bei einem nicht sichtbaren Eintrag, bei einem Eintrag, der in dieser Sprache nicht übersetzt ist, und beim Slug einer anderen Sprache. Das 404 entsteht bei jedem Lesen des Eintrags (im Template oder mit .value), solange kein Eintrag passt, nicht beim Aufruf.
useGinkoQuery
useGinkoQuery(brick, query) filtert, sortiert und blättert die sichtbaren Einträge einer Sammlung. Es liest die Daten der Seite, keine Datenbank:
const hauptspeisen = useGinkoQuery('menu', { where: { category: 'mains', price: { lte: { amount: 1500 } } }, order: 'price desc, name', limit: 10 })where nimmt pro Feld einen Wert (das Feld muss ihm gleich sein) oder eine Bedingung. Jedes Feld muss passen:
eq,ne: gleich oder ungleich. Bei einem Listenwert (f.choices,f.refs) zählt ein Element.gt,gte,lt,lte: größer oder kleiner. Geld vergleicht nachamount, eindaterangenach seinem Startdatum, Datum und Uhrzeit als Text.in: gleich einem der Werte in der Liste.contains: ein Text enthält den Text (Groß- und Kleinschreibung zählen gleich), eine Liste enthält den Wert.
order ist ein Text wie 'price desc, name' (asc ist der Standard) oder { field, dir? }. limit muss mindestens 1 sein; page beginnt bei 1. fields behält nur diese Felder, facets zählt die Werte dieser Felder über alle Treffer, und include ersetzt die IDs dieser ref-Felder durch die Einträge. Die Typen kommen aus deiner Konfiguration: Ein Feld, das der Baustein nicht hat, ist ein Typfehler in deinem Code-Editor und ein Fehler, wenn die Abfrage läuft.
useGinkoToday
useGinkoToday() liefert das heutige Datum als YYYY-MM-DD, in der Zeitzone der Seite. Der Wert ist ein Ref. Auf dem Server gilt zuerst der Wert des Servers, damit Server und Browser denselben Tag rendern. Im Browser läuft der Wert nach Mitternacht weiter, direkt nach dem Laden, jede Minute und beim Zurückkehren in den Tab. useGinko filtert mit diesem Wert. Abgelaufene Hinweise verschwinden also ohne Neuladen.
Nutze useGinkoToday() auch für eigene Datumsvergleiche, zum Beispiel „heute geöffnet“.
useGinkoSite
useGinkoSite() gibt die Angaben der Seite aus ginko.config.ts zurück: name, url (die Adresse in production), locales, mainLocale (die erste Sprache) und timeZone. Nutze es für absolute Links, zum Beispiel in JSON-LD:
const { url } = useGinkoSite()
const address = new URL(useRoute().path, url).hrefDas Modul schreibt weitere Werte nach runtimeConfig.public.ginko. Sie sind intern und können sich in jedem Release ändern. Lies die Seite darum nur über useGinkoSite().
GinkoMarkdown
<GinkoMarkdown :value> zeigt den Wert eines f.markdown-Feldes an:
<GinkoMarkdown :value="legal.data.imprint" class="prose" />Der Text liegt als Markdown vor, das Bundle liefert ihn schon als Baum. Es läuft kein Parser im Browser und kein v-html. Ein unsicherer Link (zum Beispiel javascript:) wird zu reinem Text, und ein Attribut, das der Parser nicht erlaubt (zum Beispiel style), fällt weg. Mit :components ordnest du Tags eigene Komponenten zu; eine Komponente behält die Props, die components in ginko.config.ts für sie nennt. Mehr dazu unter „Inhalte aus Dateien“.
useGinkoSeo
useGinkoSeo setzt den Kopf einer Seite: Titel (mit seo.titleTemplate der Site), Beschreibung, Open-Graph- und Twitter-Tags und JSON-LD. ginko-i18n setzt den Canonical-Link und hreflang (siehe Sprachen). Ruf es einmal pro Seite auf, mit einer Funktion, damit die Werte dem Inhalt folgen:
const route = useRoute()
const event = useGinkoEntry('events', () => route.params.slug)
useGinkoSeo(() => ({
title: event.value.data.title,
image: event.value.data.image?.url,
type: 'article',
}))| Option | Bedeutung |
|---|---|
title, description | Standard: der Name der Site und seo.description |
image | Ein Pfad unter public/ oder eine absolute URL, oder ein Objekt { url, width?, height?, alt? } (SeoImage), das auch Größe und Alt-Text angibt. Standard: seo.image. Mit erzeugten Vorschaukarten kommt das Bild in die Karte. |
type | 'website' (Standard) oder 'article' |
section | Das kleine Label über dem Titel auf der erzeugten Vorschaukarte, etwa der Bereich einer Doku-Seite. Ohne erzeugte Karte bewirkt es nichts. |
noindex | Hält die Seite aus Suchmaschinen heraus |
jsonLd | Weitere JSON-LD-Objekte. Ein Beispiel steht auf SEO und Agenten. |
Vorschaukarten. Seiten aus create-ginko bringen nuxt-og-image und seinen Renderer @takumi-rs/core mit. Dann bekommt jede Seite eine erzeugte Karte für Links in Chats und sozialen Netzwerken: Name und Logo der Site (seo.organization.logo), Titel, Beschreibung, section und das image der Seite, auf der Markenfarbe seo.color. Auf einer hellen Markenfarbe ist die Schrift dunkel. Für eine bestehende Site (Nuxt 4.6.1 oder neuer) installierst du nuxt-og-image und @takumi-rs/core und trägst 'nuxt-og-image' in modules ein. Eine Site, die auf einem Server rendert, braucht NUXT_APP_SECRET (siehe Veröffentlichen). Mit dem Modul werden die Werte der Karte einmal gelesen, wenn useGinkoSeo läuft: Ruf es auf, nachdem die Daten der Seite geladen sind. Ohne das Modul ist og:image das image der Seite oder seo.image, und Ginko lädt nichts von nuxt-og-image.
formatMoney
formatMoney(money, language) macht aus einem Geldwert einen Text. language ist ein Sprach-Tag wie de-AT, nicht der Code der Sprache: Auf einer Seite übergibst du useI18n().language. Importiere sie aus @lupinum/ginko.
| Aufruf | Ergebnis |
|---|---|
formatMoney({ amount: 1250, currency: 'EUR' }, 'de') | 12,50 € |
formatMoney({ amount: 1250, currency: 'EUR' }, 'en') | €12.50 |
formatMoney({ amount: 1250, currency: 'CHF' }, 'de') | 12,50 CHF |
formatMoney({ amount: 1250, currency: 'CHF' }, 'de-CH') | CHF 12.50 |
Alle Leerzeichen im Ergebnis sind geschützte Leerzeichen. So bricht der Preis nicht um, und Server und Browser zeigen denselben Text.
Fallback und das Attribut lang
Fehlt die Übersetzung eines Feldes in der aktuellen Sprache, zeigt das Bundle den Text der Hauptsprache. Eine veraltete Übersetzung, deren Haupttext sich danach geändert hat, bleibt, wie sie ist, bis jemand sie neu übersetzt oder bestätigt; das Studio markiert sie. In einem Baustein mit route fehlt ein Eintrag ohne übersetzten Pflichttext dagegen ganz in dieser Sprache (siehe Sprachen). Die Liste fallback nennt jedes solche Feld mit seinem Pfad. Bei einem Feld in einer Listenzeile besteht der Pfad aus Feld, Zeilen-_id und Spalte, zum Beispiel days.k3Fd9aZ0qx.dish.
Ein Screenreader liest einen fremden Text mit der falschen Aussprache, wenn die Seite ihn nicht markiert. Setze deshalb lang auf die Hauptsprache, wenn der Pfad in fallback steht.
Ginko bringt dafür useGinkoLang() mit (automatisch importiert). Es gibt eine Funktion (item, path) zurück. Die Funktion liefert den Sprach-Tag der Hauptsprache, zum Beispiel de-AT aus languages in ginko.config.ts, wenn das Feld aus dem Fallback kommt, sonst undefined:
<script setup lang="ts">
const menu = useGinko('menu')
const langOf = useGinkoLang()
</script>
<template>
<h3 v-for="item in menu.items" :key="item.id" :lang="langOf(item, 'name')">{{ item.data.name }}</h3>
</template>Für eine Listenzeile eines Einzelstücks nennst du den vollen Pfad:
<span :lang="langOf(lunch, `days.${day._id}.dish`)">{{ day.dish }}</span>Bei einem Bild heißt der Pfad des Alternativtexts image.alt, wenn das Feld image heißt. Ein Wert undefined lässt das Attribut weg.
Das Attribut lang am <html>-Element setzt ginko-i18n für dich. Siehe „Sprachen“.
Fehler und Cache
Die Seite lädt das Bundle einmal pro Anfrage. Scheitert das (Netzwerk, Serverfehler, falsches Format, zu viele Anfragen), antwortet die Seite mit 503 und nie mit 404. Auf Vercel behält ein 503 die alte, gute Seite im Cache, ein 404 würde sie ersetzen. Nur ein bestätigt fehlender Eintrag liefert 404. Das Modul schaltet für alle Seiten ISR ein (standardmäßig expiration: 60, Abfrageparameter teilen sich einen Cache-Eintrag). Deine eigenen Server-Routen unter /api/** und Ginkos dynamische Routen unter /__ginko/ laufen ohne Cache (Details: Veröffentlichen). Eine Seite, deren Bausteine alle aus Dateien kommen, fragt die Plattform nie und kann deshalb keinen 503 wegen der Plattform liefern.
error.vue weder useGinko noch andere Composables, die das Bundle brauchen. Bei einem 503 gibt es kein Bundle, und die Fehlerseite würde selbst scheitern.API-Referenz
Automatische Importe
Das Nuxt-Modul importiert diese Funktionen in jede Komponente, Seite und jedes Composable deiner Seite. brick ist der Schlüssel eines Bausteins in ginko.config.ts. Die Typen folgen aus deiner Konfiguration.
Die Regel für die Namen: Eine Funktion, die den Nuxt-Kontext braucht, beginnt mit useGinko. Rufe sie in setup oder in einem anderen Composable auf, nicht in einem Event-Handler. Eine Funktion ohne Nuxt-Kontext wird nicht automatisch importiert; du importierst sie aus @lupinum/ginko (siehe unten). Die einzige Ausnahme ist fetchGinkoMarkdown: Es braucht die eigenen Routen der Seite, aber keinen Nuxt-Kontext. Darum kannst du es überall in der Seite aufrufen.
| Funktion | Gibt zurück | Wofür |
|---|---|---|
useGinko(brick) | ComputedRef<{ data, fallback }> für ein Single, ComputedRef<{ items }> für eine Collection | Einen Baustein lesen. Collections enthalten nur sichtbare Einträge. |
useGinkoEntry(brick, slug: MaybeRefOrGetter<string | string[] | undefined>) | Readonly<Ref<GinkoItem<brick>>> | Den sichtbaren Eintrag mit diesem Slug in der aktuellen Sprache, sonst ein 404. Siehe useGinkoEntry. |
useGinkoQuery(brick, query?: MaybeRefOrGetter<Query>) | ComputedRef<{ items, total, page, pages, facets? }> | Die sichtbaren Einträge filtern, sortieren und blättern. query hat where, order, limit, page (ab 1), fields, facets und include. Siehe useGinkoQuery. |
useGinkoNeighbors(brick, id: MaybeRefOrGetter<string>, options?: { order?: Query['order'] }) | ComputedRef<{ prev?, next? }> | Die Einträge vor und nach id, für Links „zurück“ und „weiter“. order ist dasselbe wie in Query (zum Beispiel { order: 'date desc' }); ohne Angabe gilt die Reihenfolge des Bausteins. |
useGinkoNav(bricks: MaybeRefOrGetter<string[]>) | ComputedRef<NavNode[]> | Ein Navigationsbaum aus den sichtbaren Einträgen dieser Collections. |
useGinkoBreadcrumbs(bricks: MaybeRefOrGetter<string[]>) | ComputedRef<NavNode[]> | Die Knoten von der Wurzel bis zur aktuellen Seite. Leer, wenn die Seite nicht im Baum ist. |
useGinkoToday() | Ref<string> | Heute als JJJJ-MM-TT in der Zeitzone der Seite. |
useGinkoWeek() | WeekInfo | Der erste Tag der Woche und die Wochenendtage der Seite. |
useGinkoLocale() | ComputedRef<GinkoLocale> | Die aktuelle Sprache. |
useGinkoPath() | (key: GinkoPageKey, locale?: GinkoLocale) => string | Eine Funktion, die den Pfad einer Seite aus pages liefert, in der aktuellen oder einer anderen Sprache. |
useGinkoLang() | (item, path: string) => string | undefined | Eine Funktion, die den Wert für lang bei einem Feld aus dem Fallback liefert. Siehe Fallback und das Attribut lang. |
useGinkoSite() | { name, url, locales, mainLocale, timeZone } | Die Angaben der Seite. Siehe useGinkoSite. |
useGinkoSeo(input: MaybeRefOrGetter<GinkoSeoInput>) | void | Der Kopf einer Seite. Siehe useGinkoSeo. |
useGinkoMarkdown(value: MaybeRefOrGetter<MarkdownValue | undefined>) | Promise<ComputedRef<MarkdownTree | null>> | Der Baum eines Markdown-Felds, um ihn im Code zu teilen oder zu lesen. Nutze es mit await. |
fetchGinkoMarkdown(value: MarkdownValue | undefined) | Promise<MarkdownTree | null> | Dasselbe für Server-Code, zum Beispiel in useAsyncData. Nimm die Sprache in den Schlüssel auf, etwa () => `services:${locale.value}`, sonst bleiben nach einem Sprachwechsel die alten Texte stehen. |
Auch diese Typen werden automatisch importiert:
| Typ | Bedeutung |
|---|---|
GinkoItem<'brick'> | Ein Eintrag einer Collection, für Props: defineProps<{ dish: GinkoItem<'menu'> }>() |
GinkoPageKey | Ein Schlüssel aus pages |
GinkoLocale | Eine Sprache der Seite |
GinkoSeoInput | Das Argument von useGinkoSeo |
Server-Routen
Eigene Server-Routen (server/routes/, server/middleware/) bekommen einen weiteren Auto-Import:
| Funktion | Gibt zurück | Wofür |
|---|---|---|
getGinkoItems(event, brick, locale?) | Promise<GinkoServerItem[]> | Die sichtbaren Einträge einer Sammlung in einer Sprache (ohne Angabe: der Hauptsprache), in der Reihenfolge des Bausteins, jeder mit url, wenn die Sammlung eine Route hat: das, was useGinko(brick).items auf einer Seite enthält. Entwürfe und Einträge nach ihrem visible-Datum fehlen. Eine Sprache, die die Seite nicht hat, oder ein Schlüssel, der keine Sammlung ist, antwortet mit 404; eine nicht erreichbare Plattform mit 503. Für einen Feed oder eine Kalenderdatei, zum Beispiel mit rssXml aus @lupinum/ginko/basics. |
Komponenten
| Komponente | Props |
|---|---|
<GinkoMarkdown> | value (ein Markdown-Feld), components (eigene Komponenten nach Tag). Siehe GinkoMarkdown. |
<GinkoImage> | image (ein Bildfeld), sizes (Standard 100vw, wenn das Bild kleinere Kopien hat, variants), eager (sofort laden, für das Hauptbild einer Seite), frame (der benannte Rahmen des Feldes; ein Feld mit einem Seitenverhältnis braucht keinen, original zeigt das ganze Foto). Sie schreibt srcset, width, height und alt; mit Rahmen auch aspect-ratio, object-fit: cover und den Fokuspunkt, und height in der Form des Rahmens. |
@lupinum/ginko
Diese Namen importierst du selbst:
| Name | Wofür |
|---|---|
defineSite, single, collection, f | Die Konfiguration. Siehe Bausteine. |
formatMoney(money, language) | Ein Geldbetrag als Text. Nicht automatisch importiert: importiere sie dort, wo du sie brauchst. |
safeHref(href: string | undefined) | Die URL, wenn sie für href oder src sicher ist, sonst undefined. Sicher sind http, https, mailto, tel, ein Pfad der Seite (/…), ein Fragment (#…) und ein Routen-Link ($…). Nutze es für Links und Bilder aus Inhalten. |
plainText(tree: MarkdownTree) | Der Text eines Markdown-Baums ohne Formatierung, zum Beispiel für eine Meta-Beschreibung. |
wordsOf(value: MarkdownValue) | Die Zahl der Wörter eines Markdown-Werts, zum Beispiel für eine Lesezeit. Geht auch in Listen, wo der Text nicht geladen ist. |
breadcrumbJsonLd(steps, siteUrl) | Ein JSON-LD BreadcrumbList aus Schritten mit title und path (etwa den Knoten von useGinkoBreadcrumbs); siteUrl aus useGinkoSite() macht die Pfade absolut. Siehe SEO und Agenten. |
startOfWeek(date: string, week: WeekInfo) | Der erste Tag der Woche, die date enthält, als JJJJ-MM-TT. Gib useGinkoWeek() als week mit. |
defineComponents(registry) | Definiert eigene Markdown-Komponenten (Tag → Definition) für components in der Seitenkonfiguration. Siehe Komponenten. |
Es exportiert auch diese Typen. Jeder kommt aus deiner Konfiguration oder aus einer Funktion oben:
| Typ | Was er ist |
|---|---|
SiteInput, Migration, MigrationContext | Die Seitenkonfiguration, eine Migration (brick, fills, compute) und was compute bekommt. |
Field, Fields, Brick, SingleBrick, CollectionBrick | Felder und Bausteine in der gespeicherten Form (mit pos und retired, die die Plattform setzt). Damit typisierst du eigene Baustein-Fabriken. |
Task, Visibility, BrickSource, RouteTemplate, ChoiceOption | Teile eines Bausteins: eine Aufgabe, visible, source, ein Wert von route, eine Option von f.choice. |
LinkConfig, Redirect, ComponentRegistry, ComponentDef, MarkdownProfile | Teile der Konfiguration, die du in eigene Dateien auslagern kannst: linkCheck, eine Weiterleitung aus redirects, components und eine Komponente, ein Markdown-Profil. |
Locale, Currency, Label, Json | Ein Sprach-Code (de, pt-BR): der Schlüssel übersetzter Texte und das Präfix der Adresse, nicht das Sprach-Tag language. Ein ISO-4217-Code (EUR), ein Text oder ein Text pro Sprache, ein beliebiger JSON-Wert. |
DataOf, ItemOf | Die Daten der Felder eines Bausteins und ein Eintrag einer Sammlung, typisiert aus deiner Konfiguration. |
Money, ImageValue, FileValue, UnknownChoice | Werte im Bundle. UnknownChoice ist ein Auswahlwert, den ein späteres Release hinzugefügt hat; dein Code braucht dafür einen Fallback. |
MarkdownValue, MarkdownTree, MarkdownToc, TocLink | Der Wert eines Markdown-Feldes, sein Baum, sein Inhaltsverzeichnis und ein Link darin. |
NavNode | Ein Knoten von useGinkoNav. |
Query, Condition | Die Abfrage von useGinkoQuery und die Bedingung für ein Feld. |
WeekInfo | Erster Tag der Woche und Wochenendtage, aus useGinkoWeek. |
SeoImage | Die Objektform von image in useGinkoSeo: { url, width?, height?, alt? }. |
@lupinum/ginko/vue exportiert GinkoMarkdown für eine Vue-App ohne Nuxt. Es heißt wie die Nuxt-Komponente, hat aber einen kleineren Vertrag: value muss ein MarkdownTree (oder null) sein, weil es keine Texte nachlädt; und es nutzt nur die Komponenten, die du in components übergibst, keine, die ein Modul bereitstellt (etwa @lupinum/ginko-components). Übergib deine Komponenten-Registry (aus defineComponents) als registry: Eine Komponente behält nur die Props, die sie dort deklariert, ohne sie nur title, lang, dir und aria-*. Es rendert sofort und braucht kein <Suspense>. Das Nuxt-<GinkoMarkdown> ist asynchron, was Nuxt-Seiten von selbst abfangen.
@lupinum/ginko/search liest den Suchindex einer Seite: loadIndex, search und die Typen SearchHit, SearchIndex und SearchIndexJson. loadIndex ist asynchron: Es lädt auch die Wortregeln (Stemmer) der Sprache des Index, und nur diese. Ein Beispiel steht auf SEO und Agenten.
Diese Einstiegspunkte sind die öffentliche API. @lupinum/ginko/internal und @lupinum/ginko/internal/search sind für Ginkos eigene Pakete und können sich in jedem Release ändern.