Facet Template-Syntax
Facet Template-Syntax
Facet ist die Template-Sprache, die von FaceFlow für das dynamische Rendern verwendet wird.
Verwenden Sie diese Seite als primären Syntax-Leitfaden. Sie behandelt die Muster, die technische Anwender beim Schreiben von Component-Templates, Variable-Templates, List-Item-Ausgaben und anderen Laufzeitfragmenten in FaceFlow tatsächlich benötigen.
Diese Seite ist bewusst detailliert. Sie ist nicht nur eine Schnellstart-Anleitung. Sie ist das Hauptdokument für die offizielle FaceFlow-Template-Erstellung.
Syntaxfamilien
Facet-Templates kombinieren üblicherweise fünf Syntaxfamilien:
| Syntax | Rolle | Typische Verwendung |
|---|---|---|
{field} | Component-Feldausgabe | direkte Ausgabe eines Component-Felds |
{{ expr }} | escapete Ausdrucksausgabe | Seitendaten, Kontextobjekte, Filter |
{{{ expr }}} | rohe Ausgabe | vertrauenswürdige Rich-Inhalte und kontrolliertes Markup |
{{#block}}...{{/block}} | Steuerfluss | Bedingungen, Schleifen, Seitenabfragen, verzögerte Ausgabe |
[[variable-name]] | Einbettung wiederverwendbarer Fragmente | gemeinsame Template-Fragmente mit optionalen Attributen |
Die häufigste Verwechslungsquelle ist das Vermischen der Component-Feld-Kurzschreibweise mit allgemeinen Facet-Ausdrücken. Halten Sie diese Grenze klar:
- verwenden Sie
{field}wenn der Eigentümer-Feldvertrag des Components direkte Feldausgabe oder Feld-Helper unterstützt - verwenden Sie
{{ ... }}beim Rendern allgemeiner kontextabhängiger Ausdrücke - verwenden Sie
[[variable-name]]wenn die richtige Lösung Wiederverwendung ist, nicht mehr Template-Logik
Schnellstart
Das minimale nützliche Facet-Mentalmodell ist:
<section class="hero">
<h1>{{ page.title }}</h1>
{{#if summary}}
<p>{{ summary }}</p>
{{/if}}
[[sales-contact-badge]]
</section>Dieses einzelne Beispiel zeigt bereits die drei wichtigsten Bausteine:
- Ausgabe von Werten
- bedingte Struktur
- Zusammensetzung wiederverwendbarer Fragmente
Ausgabe-Syntax
Component-Feldausgabe
In Component-Templates verwendet die direkte Feldausgabe oft die Einfache-Felgen-Syntax (single-brace):
<h2>{title}</h2>
<p>{summary}</p>Verwenden Sie dies, wenn der Component-Feldvertrag einfach und feldorientiert ist.
Für helper-fähige Felder erweitert sich dieselbe Familie zu Helper-Syntax:
<img src="{heroImage.webp}" alt="{heroImage.description}">
<div data-form-embed="{contactForm}"></div>
<div data-review-embed="{serviceReview}"></div>Gehen Sie nicht davon aus, dass jedes Feld Helper-Ketten unterstützt. Prüfen Sie zuerst die Referenz des jeweiligen Feld-Eigentümers.
Escape-Ausgabe
Verwenden Sie doppelte geschweifte Klammern für normale escapete Ausgaben:
<h1>{{ page.title }}</h1>
<p>{{ summary | default("No summary available.") }}</p>Dies ist die Standardwahl für einfachen Text, Labels, URLs, dynamische skalare Werte und alle Fälle, in denen HTML escaped werden sollte.
Roh-Ausgabe
Verwenden Sie dreifache geschweifte Klammern, wenn die Quelle vertrauenswürdig ist und als HTML gerendert werden soll:
<div class="prose">
{{{ body }}}
</div>Dies ist geeignet für:
- verwalteten Rich-Text
- kontrollierte HTML-Fragmenten
- vertrauenswürdige redaktionelle Inhalte
Verwenden Sie rohe Ausgabe nicht für beliebige nicht vertrauenswürdige Zeichenketten.
Kommentare
Facet unterstützt kurze und lange Kommentare:
{{! short comment }}
{{!--
long comment
spanning multiple lines
--}}Kommentare werden niemals in die Ausgabe gerendert.
Ausdrucks-Syntax
Ausdrücke bilden die Grundlage dynamischer Template-Ausgabe. Sie ermöglichen den Zugriff auf verschachtelte Eigenschaften, das Aufrufen von methodenähnlichen Transformationsaufrufen, das Anwenden von Filtern und die Verwendung konditionaler Inline-Logik.
Punkt-Pfad-Notation
Greifen Sie mit Punktnotation auf verschachtelte Eigenschaften zu:
{{ page.title }}
{{ page.parent.title }}
{{ page.parent.parent.name }}
{{ user.language.title }}Pfeil-Notation wird ebenfalls an Stellen akzeptiert, an denen Autoren natürlich auf Objektzugriff tippen:
{{ page->parent->title }}
{{#if page->parent->hasChildren}}
...
{{/if}}
{{#each page->children as="child"}}
...
{{/each}}Verwenden Sie den Stil, der im aktuellen Template klarer ist, bleiben Sie jedoch innerhalb einer Datei konsistent.
Methodenähnliche Aufrufe
Einige Objekte bieten methodenähnliche Transformationen oder Lookups an:
{{ page.image.width(800) }}
{{ page.image.size(600, 400) }}
{{ page.image.size(600, 400).webp.url }}Dies ist häufig bei:
- Bildern
- Sammlungen
- seitenlokalisierten URLs
- seitenbewussten Abfrage-Helpern
Methodenähnliche Aufrufe sind Teil des Facet-Objektmodells, nicht beliebige Ausführung von Laufzeitcode.
Schlüssel-Wert-Parameter
Einige Helper akzeptieren benannte Parameter:
{{ page.field name="summary" }}
{{ page.children selector="limit=5" }}
{{ pages.count selector="template=blog-post" }}Verwenden Sie benannte Parameter, wenn der Helper-Vertrag sie ausdrücklich erwartet. Das hält Templates lesbar und vermeidet die Überladung positionaler Argumente.
Ternäre Ausdrücke
Facet unterstützt ternäre Ausgabe-Logik:
{{ user.isLoggedin ? "Welcome back!" : "Please sign in." }}
{{ page.summary ? page.summary : "No summary provided." }}Verwenden Sie Ternaries für kurze Ausgabentscheidungen. Wenn die Verzweigung strukturell wird, bevorzugen Sie einen Block-Helper wie {{#if}}.
Filterketten
Verwenden Sie den Pipe-Operator, um Werte zu transformieren:
{{ page.title | upper }}
{{ page.body | striptags | truncate(200, "...") }}
{{ published | date("F j, Y") }}
{{ price | currency("$", 2) }}Filter werden von links nach rechts gekettet.
Filter-Argument-Stile
Facet akzeptiert sowohl Klammerstil als auch Doppelpunktstil für Argumente:
{{ page.title | truncate(100, "...") }}
{{ page.title | truncate:100:... }}Beide Formen werden unterstützt. Bevorzugen Sie in offiziellen Templates den Klammerstil, da er klarer und leichter zu überprüfen ist.
Block-Helper
Block-Helper bieten Struktur, nicht nur skalare Ausgabe.
Facet unterstützt diese Kern-Blocktypen:
| Block | Zweck |
|---|---|
{{#if}} | bedingte Darstellung |
{{#each}} | Schleife über vorhandene Arrays oder Sammlungen |
{{#pages}} | Abfrage einer Menge von Seiten |
{{#page}} | Abfrage einer einzelnen Seite |
{{#languages}} | Iteration über verfügbare Sprachen |
{{#deferred}} | Cache-kompatible verzögerte Darstellung |
{{#if}}
Verwenden Sie {{#if}} für bedingte Darstellung:
{{#if summary}}
<p>{{ summary }}</p>
{{/if}}Mit else:
{{#if user.isLoggedin}}
<p>Hello, {{ user.name }}.</p>
{{else}}
<p>Please sign in.</p>
{{/if}}Mit elseif:
{{#if page.template == "blog-post"}}
<span class="badge">Blog</span>
{{elseif page.template == "case-study"}}
<span class="badge">Case Study</span>
{{else}}
<span class="badge">Page</span>
{{/if}}Die Variante else if wird ebenfalls akzeptiert:
{{#if page.numChildren > 10}}
<p>Many children</p>
{{else if page.numChildren > 0}}
<p>Some children</p>
{{else}}
<p>No children</p>
{{/if}}Vergleichsoperatoren
| Operator | Bedeutung | Beispiel |
|---|---|---|
== | gleich | page.template == "blog-post" |
!= | nicht gleich | page.status != 1 |
> | größer als | page.numChildren > 5 |
< | kleiner als | page.numChildren < 3 |
>= | größer oder gleich | page.sort >= 0 |
<= | kleiner oder gleich | page.sort <= 10 |
Logische Operatoren
| Operator | Bedeutung | Beispiel |
|---|---|---|
&& | UND | user.isLoggedin && user.isSuperuser |
|| | ODER | page.template == "blog-post" || page.template == "news" |
! | NICHT | !user.isGuest |
Wahrheitsregeln
Facet-Wahrheitsregeln folgen einem praxisorientierten Template-Modell:
| Wert | Wahr? |
|---|---|
null | falsch |
false | falsch |
"" | falsch |
0 | falsch |
"0" | falsch |
| leeres Array oder leere Sammlung | falsch |
| alles andere | wahr |
Wenn die Bedingungslogik Geschäftsregeln statt einfacher Darstellungsregeln übernimmt, kehren Sie zum Objektmodell zurück. Verstecken Sie kein schwaches Content-Design hinter verschachtelten if-Blöcken.
{{#each}}
Verwenden Sie {{#each}}, wenn die Sammlung bereits im aktuellen Kontext existiert:
{{#each page.children as="child"}}
<a href="{{ child.url }}">{{ child.title }}</a>
{{/each}}Mit Kurzform this:
{{#each page.images}}
<img src="{{ this.url }}" alt="{{ this.description }}">
{{/each}}Mit else:
{{#each page.children as="child"}}
<div>{{ child.title }}</div>
{{else}}
<p>No child pages found.</p>
{{/each}}Schleifen-Kontextvariablen
Innerhalb von Schleifenblöcken sind diese Werte verfügbar:
| Variable | Beschreibung |
|---|---|
loop.index | nullbasiert (zero-based) Index |
loop.index1 | einsbasiert (one-based) Index |
loop.first | true bei der ersten Iteration |
loop.last | true bei der letzten Iteration |
loop.length | Gesamte Anzahl der Elemente |
loop.even | true bei geraden Indizes |
loop.odd | true bei ungeraden Indizes |
@index | Kurzform-Alias von loop.index |
Beispiel:
{{#each page.children as="child"}}
<div class="{{#if loop.odd}}row-alt{{/if}}">
<span>{{ loop.index1 }}.</span>
{{ child.title }}
</div>
{{/each}}this vs benannte Aliase
Innerhalb von {{#each}} zeigt this immer auf das aktuelle Element.
{{#each page.children}}
<a href="{{ this.url }}">{{ this.title }}</a>
{{/each}}Wenn Lesbarkeit wichtig ist, bevorzugen Sie einen expliziten Alias:
{{#each page.children as="child"}}
<a href="{{ child.url }}">{{ child.title }}</a>
{{/each}}{{#pages}}
Verwenden Sie {{#pages}}, wenn das Template Inhalte abfragen muss statt eine bereits im Scope vorhandene Sammlung zu rendern.
{{#pages selector="template=blog-post, sort=-created, limit=5" as="post"}}
<article>
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
<time>{{ post.created | date("F j, Y") }}</time>
<p>{{ post.summary | truncate(200) }}</p>
</article>
{{else}}
<p>No posts found.</p>
{{/pages}}Typische Parameter:
| Parameter | Beschreibung |
|---|---|
selector | Query-Selector-String |
as | Schleifenvariablenname |
Verwenden Sie {{#pages}} für:
- Archive
- Listen-Seiten
- Widgets für aktuelle Inhalte
- Navigationssammlungen
- abfragegesteuerte Inhaltsblöcke
Verwenden Sie {{#each}}, wenn die Sammlung bereits existiert. Verwenden Sie {{#pages}}, wenn das Template sie abrufen muss.
{{#page}}
Verwenden Sie {{#page}}, wenn nur eine Seite abgefragt werden soll:
{{#page path="/about/"}}
<a href="{{ page.url }}">{{ page.title }}</a>
{{/page}}Unterstützte Lookup-Muster umfassen:
{{#page id="1024"}}...{{/page}}
{{#page path="/about/"}}...{{/page}}
{{#page name="contact"}}...{{/page}}
{{#page selector="template=homepage"}}...{{/page}}Innerhalb eines {{#page}}-Blocks bezieht sich page für die Dauer dieses Blocks auf die abgefragte Seite.
{{#languages}}
Verwenden Sie {{#languages}}, um sprachsensible UI wie Sprachumschalter zu erstellen:
<nav class="lang-switch">
{{#languages as="lang"}}
<a href="{{ lang.url }}" {{#if lang.isCurrent}}class="active"{{/if}}>
{{ lang.title }}
</a>
{{/languages}}
</nav>Mit einer leeren Fallback-Variante:
{{#languages as="lang"}}
<a href="{{ lang.url }}">{{ lang.title }}</a>
{{else}}
{{! single-language site }}
{{/languages}}Nützliche Spracheigenschaften umfassen:
lang.idlang.namelang.titlelang.isDefaultlang.isCurrentlang.urllang.httpUrllang.locale
{{#deferred}}
Verwenden Sie {{#deferred}}, wenn die Seite stark gecacht wird, ein Teil des Inhalts jedoch von benutzerabhängigen oder sich schnell ändernden Laufzeitdaten abhängt.
{{#deferred}}
<span>Welcome, {{ user.name }}!</span>
{{/deferred}}Mit benutzerdefinierter Skeleton-Größe:
{{#deferred skeleton-width="12rem" skeleton-height="2em"}}
<div class="user-panel">
<span>{{ user.displayName }}</span>
</div>
{{/deferred}}Verwenden Sie es für:
- benutzerbezogene Begrüßungen
- Kontopanels
- cache-sichere Personalisierung
- andere Inhalte, die nicht in eine vollständige, seitenweite gecachte HTML-Antwort fixiert werden sollten
deferred sollte bewusst eingesetzt werden. Wickeln Sie nicht standardmäßig alles Dynamische darin ein.
Kontextobjekte
Facet stellt eine Reihe von Root-Level-Kontextobjekten in jeder unterstützten Render-Oberfläche bereit.
Gängige Objekte umfassen:
pagepagesuserlanguages/langsitenowattrloopthis
Innerhalb von Component-Templates werden Component-Feldwerte ebenfalls auf Root-Ebene exponiert:
<h1>{{ title }}</h1>
{{#if showGallery}}
<div class="gallery">
{{#each images as="img"}}
<img src="{{ img.url }}">
{{/each}}
</div>
{{/if}}Die ausführliche Objekt-Referenz ist dokumentiert in:
Bild-Ausgabe-Grenze
Die Bild-Syntax hängt davon ab, woher das Bildobjekt stammt.
Component-image-Feld-Helper-Syntax
<img src="{heroImage.width(1200).webp}" alt="{heroImage.description}">Seiten- oder Abfrage-Bildobjekt-Syntax
{{#each page.images as="img"}}
<img src="{{ img.width(800).webp.url }}" alt="{{ img.description }}">
{{/each}}Component-images-Feld-Schleifen-Syntax
{{#each gallery}}
<img src="{{this.width(1200).webp}}" alt="{{this.description}}">
{{/each}}Mischen Sie diese Formen nicht leichtfertig. Das Besitzerfeld oder das Laufzeitobjekt entscheidet, welche Syntax korrekt ist.
Map-Helper-Ausgabe
Map-ähnliche Felder können helper-orientierte Ausgaben bereitstellen:
{officeLocation}
{officeLocation.width(600)}
{officeLocation.height(400)}
{officeLocation.size(800,500)}Verwenden Sie dies nur, wenn der Feldvertrag es ausdrücklich dokumentiert.
Variable-Einbettung
Variablen werden mit Doppelklammer-Syntax eingebettet:
[[site-announcement]]
[[cta-badge label="New" tone="accent"]]Innerhalb des Variable-Templates sind übergebene Werte über attr verfügbar.
Variablen sind für wiederverwendbare Fragmente gedacht, nicht um richtiges Objektdesign zu ersetzen. Wenn das Fragment zu einem Abschnittsvertrag wächst, kehren Sie zum Component-Design zurück.
Verwaltete Embed-Marker
Formulare und Reviews werden typischerweise über verwaltete Embed-Marker gerendert.
Fester Modell-Embed:
<div data-form-embed="enterprise-demo-request"></div>
<div data-review-embed="customer-success-reviews"></div>Feld-gestütztes Embed innerhalb eines wiederverwendbaren Components:
<div data-form-embed="{contactForm}"></div>
<div data-review-embed="{serviceReview}"></div>Entscheidungsregel:
- fester Embed, wenn das Template einen stabilen verwalteten Workflow besitzt
- feld-gestützter Embed, wenn Autoren das verwaltete Modell zur Authoring-Zeit wählen sollen
Nutzungsempfehlungen
- halten Sie Template-Logik lesbar
- bevorzugen Sie starke Feldverträge gegenüber cleveren Template-Tricks
- verwenden Sie
{{#if}}für Struktur, nicht zum Verbergen schwachen Content-Designs - verwenden Sie
{{#each}}, wenn die Sammlung bereits existiert - verwenden Sie
{{#pages}}, wenn das Template die Sammlung abfragen muss - verwenden Sie Variablen für kleine wiederverwendbare Fragmente
- kehren Sie zu Component- oder List-Design zurück, wenn die Template-Logik zu komplex wird
Häufige Fehler
Vermeiden Sie diese Muster:
- rohe Ausgabe verwenden, wo escapete Ausgabe sicherer ist
- Component-Feld-Kurzschreibweise und generische Facet-Ausdrücke mischen, ohne den Unterschied zu verstehen
- ein Content-Modell-Problem nur mit Conditionals lösen
- ein riesiges verschachteltes Template bauen, anstatt Objektgrenzen zu klären
- dasselbe Fragment in mehreren Templates wiederholen, anstatt eine Variable zu extrahieren
- Feld-Helper-Syntax auf Feldern verwenden, die diese nicht unterstützen
Schnelle Entscheidungsübersicht
need to show one value -> {field} or {{ expr }}
need trusted HTML output -> {{{ expr }}}
need to show content conditionally -> {{#if}}
need to loop existing items -> {{#each}}
need to query content -> {{#pages}} or {{#page}}
need language-aware navigation -> {{#languages}}
need cache-safe dynamic personalization -> {{#deferred}}
need one reusable fragment -> [[variable-name]]
need a field-specific helper output -> {field.helper}
need a full reusable section contract -> return to Component design