Zum Inhalt springen

Eigene Komponenten

Eine Datei .astro hat zwei Teile: oben zwischen --- JavaScript, das beim Build läuft, unten HTML-ähnliches Markup. Die einfachste Komponente dieser Plattform ist Lernziele:

src/components/Lernziele.astro
---
import { Aside } from '@astrojs/starlight/components';
---
<Aside type="tip" title="Lernziele">
<slot />
</Aside>

<slot /> ist der Platzhalter für das, was zwischen Öffnungs- und Schliess-Tag steht. In MDX:

import Lernziele from '@components/Lernziele.astro';
<Lernziele>
- Du kannst ...
- Du verstehst ...
</Lernziele>

Der Nutzen: Wenn du später alle Lernziel-Kästen anders aussehen lassen willst, änderst du eine Datei statt vierzig Seiten.

Mit Props gibst du einer Komponente Werte mit. Aufgabe bekommt einen Titel:

src/components/Aufgabe.astro
---
import { Aside } from '@astrojs/starlight/components';
interface Props {
titel: string;
}
const { titel } = Astro.props;
---
<Aside type="note" title={`Aufgabe ${titel}`}>
<slot />
</Aside>

Das interface Props ist TypeScript und sorgt dafür, dass der Editor meckert, wenn titel fehlt. Astro.props enthält die übergebenen Werte. In MDX: <Aufgabe titel="3.1 · Rechte setzen">...</Aufgabe>.

Loesung ist ein eingeklappter Bereich. Dafür braucht es kein JavaScript, HTML kann das mit <details>:

src/components/Loesung.astro
---
interface Props {
titel?: string;
}
const { titel = 'Lösung anzeigen' } = Astro.props;
---
<details class="loesung">
<summary>{titel}</summary>
<slot />
</details>

Das Fragezeichen in titel? macht die Prop optional, der Standardwert steht in der Zuweisung. Das Aussehen kommt aus src/styles/custom.css (nächste Seite).

Der Selbstcheck muss im Browser reagieren, wenn jemand «Antworten prüfen» klickt. Dafür hat die Komponente ein <script>-Element. Astro bündelt es und liefert es nur auf Seiten aus, die das Quiz verwenden.

src/components/Quiz.astro (gekürzt)
---
interface Frage {
frage: string;
antworten: string[];
richtig: number | number[];
erklaerung?: string;
}
interface Props {
fragen: Frage[];
}
const { fragen } = Astro.props;
---
<div class="quiz" data-quiz>
{fragen.map((f, i) => (
<fieldset data-richtig={[f.richtig].flat().join(',')}>
<legend>{i + 1}. {f.frage}</legend>
{f.antworten.map((a, j) => (
<label><input type="radio" name={`f${i}`} value={j} /> {a}</label>
))}
</fieldset>
))}
<button type="button" data-check>Antworten prüfen</button>
</div>
<script>
document.querySelectorAll('[data-quiz]').forEach((quiz) => {
quiz.querySelector('[data-check]')?.addEventListener('click', () => {
quiz.querySelectorAll('fieldset').forEach((fs) => {
const gewaehlt = Array.from(fs.querySelectorAll('input:checked')).map((i) => i.value).join(',');
fs.classList.toggle('correct', gewaehlt === fs.dataset.richtig);
});
});
});
</script>

Zwei Dinge sind hier wichtig. Erstens: Der obere Teil und das Markup laufen beim Build und erzeugen fertiges HTML, die Fragen stehen also im HTML der Seite. Zweitens: Nur das <script> läuft im Browser. Es darf nicht auf fragen zugreifen, diese Variable existiert dort nicht. Darum stehen die richtigen Antworten als data-richtig im HTML.

Die vollständige Komponente mit Mehrfachauswahl, Erklärungen und Punktestand liegt im Repository dieser Plattform unter src/components/Quiz.astro.

Ein Import wie ../../../components/Quiz.astro hängt von der Ordnertiefe ab und bricht beim Verschieben einer Seite. Ein Alias löst das:

tsconfig.json
{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@components/*": ["src/components/*"]
}
}
}

Danach gilt überall import Quiz from '@components/Quiz.astro';. Astro liest die paths aus tsconfig.json und löst sie beim Build auf.

  1. Ordner src/components/ anlegen und Lernziele.astro, Aufgabe.astro, Loesung.astro wie oben erstellen.
  2. Alias in tsconfig.json eintragen, Dev-Server neu starten (Alias-Änderungen werden nicht live übernommen).
  3. In 01-hallo.mdx die drei Komponenten einsetzen.
Lösung anzeigen
src/components/Merksatz.astro
---
interface Props {
titel?: string;
}
const { titel = 'Merke' } = Astro.props;
---
<blockquote class="merksatz">
<strong>{titel}:</strong> <slot />
</blockquote>

Selbstcheck · 3 Fragen

1. Wann läuft der Code zwischen den --- einer .astro-Datei?
2. Was ist ein Slot?
3. Warum stehen die richtigen Quiz-Antworten als data-Attribut im HTML?