Mehrteilige Inhalte
Hugo bietet Mittel, um Inhalte nach den eigenen Vorstellungen zu strukturieren. Um diese Möglichkeit zu veranschaulichen, wird ein neuer Typ von Inhalten definiert: Artikel, die aus mehreren Teilen bestehen oder Teil einer Serie sind.
Hier werden verschiedene Einzelseiten zu einer Serie zusammengefasst. Dieser Fall unterscheidet sich von dem, wo eine Einzelseite aus mehreren Markup-Dateien zusammengesetzt wird. Dies ist im Rahmen von Page Bundles möglich, bei denen andere Markup-Dateien als Ressourcen verwendet werden können.
Layouts
Bei mehrteiligen Artikeln oder Artikel-Serien handelt es sich im Wesentlichen um Sektionen. Zuerst erstellen wir ein Verzeichnis, das die Markup-Dateien der einzelnen Teile beinhaltet. Die Überblicksseite unter _index.md
enthält eine Einführung in die Thematik und verlinkt auf die Teilartikel der Serie. Die Einzelseiten verlinken auf die vorherigen und folgenden Teile sowie auf die Einstiegsseite.
Es ist sinnvoll, diesen Aufbau in Form spezifischer Layouts abzubilden. Dazu legt man direkt unter layouts/
ein eigenes Unterverzeichnis an, das man beispielsweise article-series
nennt. Für die Einstiegsseite erstellen wir list.html
, für die Teilseiten single.html
.
Zunächst stellt sich die Frage, unter welchen Bedingungen die definierten Layouts für unsere mehrteiligen Inhalte greifen. Sektionen zeichnen sich unter anderem dadurch aus, dass Layouts für sie in der Regel automatisch angewendet werden. Ein Beispiel zeigt, warum das in diesem Fall nicht funktioniert.
Angenommen wir wollen eine Artikel-Serie über die Geschichte der Science Fiction schreiben und legen dafür content/science-fiction/history-of-science-fiction/
an. Standardmäßig würden hier die Layouts aus layouts/science-fiction/
angewendet. Doch was wir eigentlich wollen, ist ein spezielles Layout für mehrteilige Artikel, das auch für Serien genutzt werden kann, die nichts mit Science Fiction zu tun haben.
Layouts werden nicht nur für Top-Level-Sections automatisch genutzt. Solange ein Verzeichnis eine _index.md
enthält, prüft Hugo, ob es ein gleichnamiges Verzeichnis unter layouts/
existiert, und verwendet gegebenenfalls dessen Layouts. Wir könnten also layouts/history-of-science-fiction/
erstellen. Aber wir möchten etwas weniger Spezifisches – ein Layout für alle Artikel-Serien.
Damit die Layouts unter layouts/article-series/
genutzt werden, haben wir zwei Möglichkeiten:
- Eine eigene Top-Level-Section erstellen: Falls es zur Struktur der Seite passt, können wir eine Top-Level-Section
content/article-series/
anlegen. So würden die Layouts unterlayout/article-series/
automatisch für alle Inhalte in dieser Section angewendet. - Den Typ manuell in der Frontmatter festlegen: Um mehrteilige Artikel für beliebige Sektionen nutzen zu können, ist ein manuellerer Ansatz erforderlich. Dazu legen wir den Typ in der Frontmatter der Markup-Dateien fest.
type: article-series
Dieses Feld überschreibt den standardmäßig aus der Zugehörigkeit zur Section abgeleiteten Typ und sorgt dafür, dass das passende Layout aus dem gleichnamigen Verzeichnis unter layouts/
verwendet wird.
Damit wir den Typ nicht für jede Seite einzeln definieren müssen, können wir uns die
Verwandtschaftsbeziehungen zwischen Seiten zunutze machen, um uns die Arbeit zu erleichtern. Die Index-Seite einer Sektion wird als übergeordnete Seite (Parent
) der Einzelseiten betrachtet, die sich in derselben Sektion befinden.
Zwar erben Einzelseiten nicht automatisch den Typ der Index-Seite, aber mit dem cascade-Feld können wir Frontmatter-Werte an alle Nachfahren weitergeben:
cascade:
type: article-series
Um die Reihenfolge der Seiten in der Serie festzulegen, kann in der Frontmatter der Einzelseiten ein partNumber
-Wert hinzugefügt werden:
partNumber: 6
Dieser Wert hat für Hugo an sich keine besondere Bedeutung, kann jedoch auf der Index-Seite zur Sortierung oder auf den Einzelseiten zur Navigation verwendet werden. Die nächste Aufgabe ist es, partielle Layouts zu erstellen, die diese Werte nutzen.
Geordneter Überblick über die Teile
Auf der Index-Seite sollten die Teile der Serie in geordneter Form aufgelistet werden. Die Auflistung sollte dabei einem bestimmten Schema folgen, zum Beispiel:
Teil n: Titel
Der Lösung können wir uns annähern, wenn wir uns überlegen, wie die Auflistung in HTML übersetzt wird. Das könnte mit einigen Platzhaltern in etwa so aussehen:
<ul>
<li>
Teil PART-NUMBER: <a href="LINK-TO-PART">TITLE-of-PART</a>
</li>
</ul>
Um die erforderlichen Informationen zu sammeln, greifen wir auf die Frontmatter der Seiten zu. Zuerst benötigen wir jedoch alle Einzelseiten im gegebenen Kontext. In der Template-Sprache wird der
Kontext durch .
bezeichnet, und die Sammlung aller Einzelseiten der Sektion erhalten wir mit der
RegularPages-Methode.
Um die Seiten nach unserem partNumber
-Feld zu sortieren, verwenden wir die
ByParam-Methode wobei wir den zu sortierenden Parameter als String-Argument übergeben. Nun können wir uns die Seiten in der gewünschten Reihenfolge vornehmen, um die Listeneinträge zu erzeugen:
<ul>
{{ range .RegularPages.ByParam "partNumber" }}
<li>
Teil PART-NUMBER: <a href="LINK-TO-PART">TITLE-of-PART</a>
</li>
{{ end }}
</ul>
Die für die Platzhalter benötigten Informationen entnehmen wir der Frontmatter der Seiten. Innerhalb der Schleife verweist .
auf die jeweilige Einzelseite. Mit Title
erhalten wir den Titel und mit .Permalink
die URL zur Seite. partNumber
ist kein vordefiniertes Feld, wir können seinen Wert aber unter .Params
abrufen.
Diese Bestandteile können wir nun in unserem Layout für die Index-Seite zusammenführen. layouts/article-series/list.html
könnte folgendermaßen aussehen:
{{ define "main" }}
<main>
<div>
<h1>{{ .Title }}</h1>
{{ .Content }}
<ul>
{{ range .RegularPages.ByParam "partNumber" }}
<li>
Teil {{ .Params.partNumber }}: <a href="{{ .Permalink }}">{{ .Title }}</a>
</li>
{{ end }}
</ul>
</div>
</main>
{{ end }}
Es empfiehlt sich, die Layout-Komponenten, die innerhalb von Artikel-Serien verwendet werden, als partielle Layouts zu speichern, um sie bei Bedarf wiederzuverwenden. Dazu kann zum Beispiel das Verzeichnis layouts/partials/articles
erstellt werden.
Den Listenabschnitt unseres Layouts speichern wir als partielle Vorlage beispielsweise unter series-overview.html
:
<ul>
{{ range .RegularPages.ByParam "partNumber" }}
<li>
Teil {{ .Params.partNumber }}: <a href="{{ .Permalink }}">{{ .Title }}</a>
</li>
{{ end }}
</ul>
Schließlich binden wir das partielle Layout im Haupt-Layout ein und übergeben ihm den aktuellen Kontext:
{{ define "main" }}
<main>
<div>
<h1>{{ .Title }}</h1>
{{ .Content }}
{{ partial "articles/series-overview.html" . }}
</div>
</main>
{{ end }}
Navigation zwischen den Teilen
Es sollte möglich sein, bequem zwischen den Teilen zu navigieren – schon allein, um Besuchern der Seite die interne Logik zu vermitteln. Über entsprechende Buttons sollte man sich in der Artikelserie vor und zurück bewegen und zur Index-Seite springen können.
Zu diesem Zweck müssen wir für jede Seite der Serie die vorherige und die nächste Seite bestimmen. Da die Logik etwas komplexer ist, definieren wir zunächst einige aussagekräftige Variablen:
{{ $current_part_number := .Params.partNumber }}
{{ $indexPage := .Parent }}
{{ $pages_sorted_by_part_number := sort .CurrentSection.Pages "Params.partNumber" "asc" }}
Als nächstes initialisieren wir Variablen, die die zu ermittelnden Daten speichern. Für $prevEntry
vergeben wir einen Startwert, der für den ersten Teil der Serie genutzt wird.
{{ $prevEntry := $indexPage }}
{{ $nextEntry := "" }}
Nun kommen wir zum eigentlichen Algorithmus. Wir iterieren über alle Seiten und prüfen dabei, ob es sich um die derzeit bearbeitete Seite handelt:
{{ range $i, $page := $pages_sorted_by_part_number }}
{{ if eq $page.Params.partNumber $current_part_number }}
(...)
{{ end }}
{{ end }}
Sobald die aktuelle Seite gefunden wurde, kennen wir auch den Index ihrer Vorgänger- und Nachfolger-Seite. Mithilfe der index-Funktion können wir die jeweiligen Seiten aus der sortierten Sammlung abrufen:
{{ range $i, $page := $pages_sorted_by_part_number }}
{{ if eq $page.Params.partNumber $current_part_number }}
{{ $prevEntry = (index $pages_sorted_by_part_number (sub $i 1)) }}
{{ $nextEntry = (index $pages_sorted_by_part_number (add $i 1)) }}
{{ end }}
{{ end }}
Eine Besonderheit liegt allerdings in der Handhabung der ersten und letzten Seite der Serie. Die erste Seite sollte die Index-Seite als Vorgänger haben, und die letzte Seite hat keine Nachfolger-Seite. Dies berücksichtigen wir, indem wir die Variablen zunächst für diese Fälle initialisieren und anschließend sicherstellen, dass sie nur überschrieben werden, wenn diese Bedingungen nicht zutreffen:
{{ range $i, $page := $pages_sorted_by_part_number }}
{{ if eq $page.Params.partNumber $current_part_number }}
{{ if gt $i 0 }}
{{ $prevEntry = (index $pages_sorted_by_part_number (sub $i 1)) }}
{{ end }}
{{ if lt $i (sub (len $pages_sorted_by_part_number) 1) }}
{{ $nextEntry = (index $pages_sorted_by_part_number (add $i 1)) }}
{{ end }}
{{ break }}
{{ end }}
{{ end }}
Der {{ break }}
beendet die Schleife, sobald die aktuelle Seite gefunden wurde. Da in den folgenden Durchläufen die Bedingung nicht mehr erfüllt sein kann, sparen wir uns so unnötige Iterationen.
Den obigen Code können wir erneut als partielles Layout speichern und anschließend in das Layout der einzelnen Artikelteile layouts/article-series/single.html
einfügen.
Suchmaschinenoptimierung
Aus Sicht der Suchmaschinenoptimierung (SEO) gibt es ein paar Dinge zu beachten. Die Strukturierung der Artikelserie als eigene Sektion bringt bereits den Vorteil einer optimierten URL-Struktur: domain.com/.../topic/
für die Überblicksseite, domain.com/.../topic/part-n-subtopic
für die Einzelseiten. Diese klare Struktur hilft Suchmaschinen, die Zusammengehörigkeit der Seiten zu erkennen.
Ein weiterer zentraler SEO-Faktor sind interne Verlinkungen, die dem Webcrawler die thematischen Zusammenhänge verdeutlichen. Dies erreichen wir durch die Verlinkung zu vorausgehenden und nachfolgenden Teilen auf den Einzelseiten sowie durch die Navigation auf der Überblicksseite.
Für die Überblicksseite sollten relevante Schlagwörter und Kategorien vergeben werden, damit die Serie auch in den bei den thematisch passenden Stichwörtern eingeordnet wird. Während die Überblicksseite eine allgemeine Beschreibung der Serie enthalten sollte, kann jede Einzelseite auf ihren spezifischen Inhalt abgestimmte Beschreibungen bieten. Falls die Zugehörigkeit zur Serie besonders hervorgehoben werden soll, könnten Titel wie [Titel der Serie] - Teil n: [Titel des Teils]
verwendet werden, um die Verbindung zu verdeutlichen.