Syntaxe des templates Facet

Syntaxe des templates Facet

Facet est le langage de template utilise par FaceFlow pour le rendu dynamique.

Utilisez cette page comme guide principal de syntaxe. Elle couvre les patterns dont les utilisateurs techniques ont reellement besoin lorsqu'ils ecrivent des templates de Component, des templates de Variable, des sorties d'items de List et d'autres fragments runtime dans FaceFlow.

Cette page est volontairement detaillee. Ce n'est pas seulement un guide de demarrage rapide. C'est le document principal de syntaxe pour l'ecriture officielle de templates FaceFlow.

Familles de syntaxe

Les templates Facet combinent generalement cinq familles de syntaxe :

SyntaxeRoleUsage typique
{field}sortie de champ de Componentsortie directe depuis un champ de Component
{{ expr }}sortie d'expression echappeedonnees de page, objets de contexte, filtres
{{{ expr }}}sortie brutecontenu riche de confiance et markup controle
{{#block}}...{{/block}}flux de controleconditions, boucles, requetes de page, sortie differee
[[variable-name]]insertion de fragment reutilisablefragments partages avec attributs optionnels

La confusion la plus frequente vient du melange entre le raccourci de champ de Component et les expressions Facet generales. Gardez cette frontiere claire :

  • utilisez {field} lorsque le contrat du champ de Component prend en charge une sortie directe du champ ou des helpers de champ
  • utilisez {{ ... }} pour rendre des expressions generales dependantes du contexte
  • utilisez [[variable-name]] lorsque la bonne solution est la reutilisation, pas plus de logique de template

Demarrage rapide

Le modele mental minimum utile pour Facet est :

<section class="hero">
  <h1>{{ page.title }}</h1>

  {{#if summary}}
    <p>{{ summary }}</p>
  {{/if}}

  [[sales-contact-badge]]
</section>

Ce seul exemple montre deja les trois blocs les plus importants :

  • sortie de valeur
  • structure conditionnelle
  • composition de fragments reutilisables

Syntaxe de sortie

Sortie de champ de Component

Dans les templates de Component, la sortie directe de champ utilise souvent la syntaxe a simples accolades :

<h2>{title}</h2>
<p>{summary}</p>

Utilisez cette forme lorsque le contrat du champ de Component est simple et centre sur le champ.

Pour les champs qui prennent en charge des helpers, cette meme famille de syntaxe s'etend en syntaxe helper :

<img src="{heroImage.webp}" alt="{heroImage.description}">
<div data-form-embed="{contactForm}"></div>
<div data-review-embed="{serviceReview}"></div>

Ne supposez pas que tous les champs prennent en charge le chaining de helpers. Verifiez d'abord la reference du champ proprietaire.

Sortie d'expression echappee

Utilisez les doubles accolades pour une sortie echappee normale :

<h1>{{ page.title }}</h1>
<p>{{ summary | default("No summary available.") }}</p>

C'est le choix par defaut pour le texte simple, les labels, les URLs, les valeurs scalaires dynamiques et tous les cas ou le HTML doit etre echappe.

Sortie brute

Utilisez les triples accolades lorsque la source est de confiance et doit etre rendue comme HTML :

<div class="prose">
  {{{ body }}}
</div>

Cela convient a :

  • du rich text gere
  • des fragments HTML controles
  • du contenu editorial de confiance

N'utilisez pas la sortie brute pour des chaines arbitraires non fiables.

Commentaires

Facet prend en charge les commentaires courts et longs :

{{! commentaire court }}

{{!--
commentaire long
sur plusieurs lignes
--}}

Les commentaires ne sont jamais rendus dans la sortie.

Syntaxe d'expression

Les expressions sont la base du rendu dynamique. Elles permettent d'acceder a des proprietes imbriquees, d'appeler des methodes de type helper, d'appliquer des filtres et d'utiliser une logique conditionnelle inline.

Notation en chemin par points

Accedez aux proprietes imbriquees avec la notation par points :

{{ page.title }}
{{ page.parent.title }}
{{ page.parent.parent.name }}
{{ user.language.title }}

La notation avec fleches est egalement acceptee dans les endroits ou les auteurs tapent naturellement un acces objet :

{{ page->parent->title }}
{{#if page->parent->hasChildren}}
  ...
{{/if}}
{{#each page->children as="child"}}
  ...
{{/each}}

Utilisez le style le plus clair dans le template courant, mais restez coherent au sein d'un meme fichier.

Appels de type methode

Certains objets exposent des transformations ou des recherches de type methode :

{{ page.image.width(800) }}
{{ page.image.size(600, 400) }}
{{ page.image.size(600, 400).webp.url }}

Ces appels sont courants pour :

  • les images
  • les collections
  • les URLs localisees de page
  • les helpers de requete conscients de la page

Les appels de type methode font partie du modele objet de Facet, pas d'une execution arbitraire de code runtime.

Parametres cle-valeur

Certains helpers acceptent des parametres nommes :

{{ page.field name="summary" }}
{{ page.children selector="limit=5" }}
{{ pages.count selector="template=blog-post" }}

Utilisez les parametres nommes lorsque le contrat du helper les attend explicitement. Cela garde les templates lisibles et evite de surcharger des arguments positionnels.

Expressions ternaires

Facet prend en charge la logique ternaire :

{{ user.isLoggedin ? "Welcome back!" : "Please sign in." }}
{{ page.summary ? page.summary : "No summary provided." }}

Utilisez les ternaires pour de petites decisions de sortie. Si le branchement devient structurel, preferez un block helper comme {{#if}}.

Chaines de filtres

Utilisez l'operateur pipe pour transformer les valeurs :

{{ page.title | upper }}
{{ page.body | striptags | truncate(200, "...") }}
{{ published | date("F j, Y") }}
{{ price | currency("$", 2) }}

Les filtres s'appliquent de gauche a droite.

Styles d'arguments de filtre

Facet accepte a la fois le style avec parentheses et le style avec deux-points :

{{ page.title | truncate(100, "...") }}
{{ page.title | truncate:100:... }}

Les deux formes sont prises en charge. Preferez le style avec parentheses dans les templates officiels parce qu'il est plus clair et plus facile a relire.

Block helpers

Les block helpers fournissent de la structure, pas seulement une sortie scalaire.

Facet prend en charge ces types de blocs principaux :

BlocFonction
{{#if}}rendu conditionnel
{{#each}}boucle sur des tableaux ou collections existants
{{#pages}}requete sur un ensemble de pages
{{#page}}requete sur une seule page
{{#languages}}boucle sur les langues disponibles
{{#deferred}}rendu differe compatible cache

{{#if}}

Utilisez {{#if}} pour le rendu conditionnel :

{{#if summary}}
  <p>{{ summary }}</p>
{{/if}}

Avec else :

{{#if user.isLoggedin}}
  <p>Hello, {{ user.name }}.</p>
{{else}}
  <p>Please sign in.</p>
{{/if}}

Avec 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}}

La variante else if est egalement acceptee :

{{#if page.numChildren > 10}}
  <p>Many children</p>
{{else if page.numChildren > 0}}
  <p>Some children</p>
{{else}}
  <p>No children</p>
{{/if}}

Operateurs de comparaison

OperateurSignificationExemple
==egal apage.template == "blog-post"
!=different depage.status != 1
>superieur apage.numChildren > 5
<inferieur apage.numChildren < 3
>=superieur ou egalpage.sort >= 0
<=inferieur ou egalpage.sort <= 10

Operateurs logiques

OperateurSignificationExemple
&&ETuser.isLoggedin && user.isSuperuser
||OUpage.template == "blog-post" || page.template == "news"
!NON!user.isGuest

Regles de truthiness

La truthiness de Facet suit un modele pratique oriente template :

ValeurTruthy ?
nullfaux
falsefaux
""faux
0faux
"0"faux
tableau vide ou collection videfaux
tout le restevrai

Si la logique conditionnelle commence a porter des regles metier plutot que de simples regles d'affichage, revenez au modele objet. Ne cachez pas un mauvais design de contenu derriere des blocs if imbriques.

{{#each}}

Utilisez {{#each}} lorsque la collection existe deja dans le contexte courant :

{{#each page.children as="child"}}
  <a href="{{ child.url }}">{{ child.title }}</a>
{{/each}}

Avec le raccourci this :

{{#each page.images}}
  <img src="{{ this.url }}" alt="{{ this.description }}">
{{/each}}

Avec else :

{{#each page.children as="child"}}
  <div>{{ child.title }}</div>
{{else}}
  <p>No child pages found.</p>
{{/each}}

Variables de contexte de boucle

A l'interieur des blocs de boucle, ces valeurs sont disponibles :

VariableDescription
loop.indexindex base zero
loop.index1index base un
loop.firstvrai a la premiere iteration
loop.lastvrai a la derniere iteration
loop.lengthnombre total d'elements
loop.evenvrai sur les index pairs
loop.oddvrai sur les index impairs
@indexalias court de loop.index

Exemple :

{{#each page.children as="child"}}
  <div class="{{#if loop.odd}}row-alt{{/if}}">
    <span>{{ loop.index1 }}.</span>
    {{ child.title }}
  </div>
{{/each}}

this vs alias nommes

Dans {{#each}}, this pointe toujours vers l'element courant.

{{#each page.children}}
  <a href="{{ this.url }}">{{ this.title }}</a>
{{/each}}

Si la lisibilite compte, preferez un alias explicite :

{{#each page.children as="child"}}
  <a href="{{ child.url }}">{{ child.title }}</a>
{{/each}}

{{#pages}}

Utilisez {{#pages}} lorsque le template doit interroger du contenu plutot que rendre une collection deja presente dans la portee.

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

Parametres typiques :

ParametreDescription
selectorchaine de selecteur de requete
asnom de variable de boucle

Utilisez {{#pages}} pour :

  • les archives
  • les pages de listing
  • les widgets de contenu recent
  • les collections de navigation
  • les blocs de contenu pilotes par requete

Utilisez {{#each}} lorsque la collection existe deja. Utilisez {{#pages}} lorsque le template doit l'interroger.

{{#page}}

Utilisez {{#page}} lorsqu'une seule page doit etre interrogee :

{{#page path="/about/"}}
  <a href="{{ page.url }}">{{ page.title }}</a>
{{/page}}

Les patterns de recherche pris en charge incluent :

{{#page id="1024"}}...{{/page}}
{{#page path="/about/"}}...{{/page}}
{{#page name="contact"}}...{{/page}}
{{#page selector="template=homepage"}}...{{/page}}

Dans un bloc {{#page}}, page fait reference a la page interrogee pendant la duree de ce bloc.

{{#languages}}

Utilisez {{#languages}} pour construire une UI consciente des langues comme un switcher de langue :

<nav class="lang-switch">
  {{#languages as="lang"}}
    <a href="{{ lang.url }}" {{#if lang.isCurrent}}class="active"{{/if}}>
      {{ lang.title }}
    </a>
  {{/languages}}
</nav>

Avec un fallback vide :

{{#languages as="lang"}}
  <a href="{{ lang.url }}">{{ lang.title }}</a>
{{else}}
  {{! site monolingue }}
{{/languages}}

Proprietes utiles de langue :

  • lang.id
  • lang.name
  • lang.title
  • lang.isDefault
  • lang.isCurrent
  • lang.url
  • lang.httpUrl
  • lang.locale

{{#deferred}}

Utilisez {{#deferred}} lorsque la page est fortement mise en cache mais qu'une partie du contenu depend d'un utilisateur ou d'un etat runtime qui change rapidement.

{{#deferred}}
  <span>Welcome, {{ user.name }}!</span>
{{/deferred}}

Avec un skeleton personnalise :

{{#deferred skeleton-width="12rem" skeleton-height="2em"}}
  <div class="user-panel">
    <span>{{ user.displayName }}</span>
  </div>
{{/deferred}}

Utilisez-le pour :

  • les salutations dependantes de l'utilisateur
  • les panneaux de compte
  • la personnalisation compatible cache
  • les autres contenus qui ne doivent pas etre figes dans une reponse HTML complete mise en cache

deferred doit etre utilise intentionnellement. N'enveloppez pas tout ce qui est dynamique par defaut.

Objets de contexte

Facet expose un ensemble d'objets de contexte de niveau racine dans chaque surface de rendu prise en charge.

Les objets courants incluent :

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

Dans les templates de Component, les valeurs de champ de Component sont egalement exposees au niveau racine :

<h1>{{ title }}</h1>

{{#if showGallery}}
  <div class="gallery">
    {{#each images as="img"}}
      <img src="{{ img.url }}">
    {{/each}}
  </div>
{{/if}}

La reference detaillee des objets est documentee dans :

Frontiere de sortie des images

La syntaxe d'image depend de l'origine de l'objet image.

Syntaxe helper de champ image de Component

<img src="{heroImage.width(1200).webp}" alt="{heroImage.description}">

Syntaxe objet image de page ou de requete

{{#each page.images as="img"}}
  <img src="{{ img.width(800).webp.url }}" alt="{{ img.description }}">
{{/each}}

Syntaxe de boucle de champ images de Component

{{#each gallery}}
  <img src="{{this.width(1200).webp}}" alt="{{this.description}}">
{{/each}}

Ne melangez pas ces formes a la legere. Le champ proprietaire ou l'objet runtime decide quelle syntaxe est correcte.

Sortie helper de map

Les champs de type map peuvent exposer une sortie orientee helper :

{officeLocation}
{officeLocation.width(600)}
{officeLocation.height(400)}
{officeLocation.size(800,500)}

Utilisez cela uniquement lorsque le contrat du champ le documente explicitement.

Insertion de Variables

Les Variables sont inserees avec la syntaxe a doubles crochets :

[[site-announcement]]
[[cta-badge label="New" tone="accent"]]

Dans le template de la Variable, les valeurs passees sont disponibles via attr.

Les Variables servent aux fragments reutilisables, pas a remplacer un bon design objet. Si le fragment devient un contrat de section, revenez a un design de Component.

Marqueurs d'embed geres

Les Forms et les Reviews sont generalement rendus via des marqueurs d'embed geres.

Embed de modele fixe :

<div data-form-embed="enterprise-demo-request"></div>
<div data-review-embed="customer-success-reviews"></div>

Embed pilote par champ dans un Component reutilisable :

<div data-form-embed="{contactForm}"></div>
<div data-review-embed="{serviceReview}"></div>

Regle de decision :

  • embed fixe lorsque le template possede un workflow gere stable
  • embed pilote par champ lorsque les auteurs doivent choisir le modele gere au moment de l'edition

Conseils d'usage

  • gardez la logique de template lisible
  • preferez des contrats de champ solides aux astuces de template trop clever
  • utilisez {{#if}} pour la structure, pas pour masquer un design de contenu faible
  • utilisez {{#each}} lorsque la collection existe deja
  • utilisez {{#pages}} lorsque le template doit interroger la collection
  • utilisez les Variables pour de petits fragments reutilisables
  • revenez au design de Component ou de List lorsque la logique de template devient trop complexe

Erreurs frequentes

Evitez ces patterns :

  • utiliser la sortie brute la ou une sortie echappee est plus sure
  • melanger le raccourci de champ de Component et les expressions Facet generiques sans comprendre la difference
  • resoudre un probleme de modele de contenu uniquement avec des conditionnels
  • construire un immense template imbrique au lieu de clarifier les frontieres d'objet
  • repeter le meme fragment dans plusieurs templates au lieu d'extraire une Variable
  • utiliser une syntaxe helper sur des champs qui ne la prennent pas en charge

Guide de decision rapide

besoin d'afficher une valeur               -> {field} ou {{ expr }}
besoin d'une sortie HTML de confiance      -> {{{ expr }}}
besoin d'afficher conditionnellement       -> {{#if}}
besoin de boucler sur des items existants  -> {{#each}}
besoin d'interroger du contenu             -> {{#pages}} ou {{#page}}
besoin d'une navigation multilingue        -> {{#languages}}
besoin d'une personnalisation cache-safe   -> {{#deferred}}
besoin d'un fragment reutilisable          -> [[variable-name]]
besoin d'une sortie helper specifique      -> {field.helper}
besoin d'un contrat de section reutilisable complet -> revenir au design de Component

Continuer