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:

SyntaxRolleTypische Verwendung
{field}Component-Feldausgabedirekte Ausgabe eines Component-Felds
{{ expr }}escapete AusdrucksausgabeSeitendaten, Kontextobjekte, Filter
{{{ expr }}}rohe Ausgabevertrauenswürdige Rich-Inhalte und kontrolliertes Markup
{{#block}}...{{/block}}SteuerflussBedingungen, Schleifen, Seitenabfragen, verzögerte Ausgabe
[[variable-name]]Einbettung wiederverwendbarer Fragmentegemeinsame 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:

BlockZweck
{{#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

OperatorBedeutungBeispiel
==gleichpage.template == "blog-post"
!=nicht gleichpage.status != 1
>größer alspage.numChildren > 5
<kleiner alspage.numChildren < 3
>=größer oder gleichpage.sort >= 0
<=kleiner oder gleichpage.sort <= 10

Logische Operatoren

OperatorBedeutungBeispiel
&&UNDuser.isLoggedin && user.isSuperuser
||ODERpage.template == "blog-post" || page.template == "news"
!NICHT!user.isGuest

Wahrheitsregeln

Facet-Wahrheitsregeln folgen einem praxisorientierten Template-Modell:

WertWahr?
nullfalsch
falsefalsch
""falsch
0falsch
"0"falsch
leeres Array oder leere Sammlungfalsch
alles anderewahr

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:

VariableBeschreibung
loop.indexnullbasiert (zero-based) Index
loop.index1einsbasiert (one-based) Index
loop.firsttrue bei der ersten Iteration
loop.lasttrue bei der letzten Iteration
loop.lengthGesamte Anzahl der Elemente
loop.eventrue bei geraden Indizes
loop.oddtrue bei ungeraden Indizes
@indexKurzform-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:

ParameterBeschreibung
selectorQuery-Selector-String
asSchleifenvariablenname

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.id
  • lang.name
  • lang.title
  • lang.isDefault
  • lang.isCurrent
  • lang.url
  • lang.httpUrl
  • lang.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:

  • page
  • pages
  • user
  • languages / lang
  • site
  • now
  • attr
  • loop
  • this

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

Weiter