Sintaxis de plantillas de Facet

Sintaxis de plantillas de Facet

Facet es el lenguaje de plantillas que usa FaceFlow para el renderizado dinámico.

Utilice esta página como la guía de sintaxis principal. Cubre los patrones que los usuarios técnicos realmente necesitan al escribir plantillas de Componentes, plantillas de Variables, salida de ítems de Lista y otros fragmentos en tiempo de ejecución dentro de FaceFlow.

Esta página es intencionadamente detallada. No es solo una guía de inicio rápido. Es el documento principal de sintaxis para la redacción oficial de plantillas de FaceFlow.

Familias de sintaxis

Las plantillas Facet suelen combinar cinco familias de sintaxis:

SintaxisFunciónUso típico
{field}Salida de campo de Componentesalida directa desde un campo de Componente
{{ expr }}salida de expresión escapadadatos de la página, objetos de contexto, filtros
{{{ expr }}}salida sin escaparcontenido enriquecido confiable y marcado controlado
{{#block}}...{{/block}}flujo de controlcondiciones, bucles, consultas de páginas, salida diferida
[[variable-name]]inclusión de fragmento reutilizablefragmentos de plantilla compartidos con atributos opcionales

La fuente más común de confusión es mezclar la notación abreviada de campo de Componente con expresiones generales de Facet. Mantenga clara esa frontera:

  • use {field} cuando el contrato del campo del Componente propietario soporte salida directa de campos o helpers de campo
  • use {{ ... }} cuando esté renderizando expresiones generales conscientes del contexto
  • use [[variable-name]] cuando la solución correcta sea la reutilización, no más lógica de plantilla

Inicio rápido

El modelo mental mínimo y útil de Facet es:

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

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

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

Ese único ejemplo ya muestra los tres bloques constructivos más importantes:

  • salida de valores
  • estructura condicional
  • composición de fragmentos reutilizables

Sintaxis de salida

Salida de campo de Componente

En las plantillas de Componentes, la salida directa de campos suele usar la sintaxis de llave simple:

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

Use esto cuando el contrato del campo del Componente sea simple y orientado a campos.

Para campos que admiten helpers, la misma familia se extiende a la sintaxis de helper:

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

No asuma que todos los campos admiten encadenamiento de helpers. Verifique primero la referencia del campo propietario.

Salida de expresión escapada

Use dobles llaves para la salida escapada normal:

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

Esta es la elección predeterminada para texto plano, etiquetas, URLs, valores escalares dinámicos y todos los casos donde el HTML debe ser escapado.

Salida sin escapar

Use triples llaves cuando la fuente sea confiable y deba renderizarse como HTML:

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

Esto es apropiado para:

  • texto enriquecido gestionado
  • fragmentos HTML controlados
  • contenido editorial confiable

No use salida sin escapar para cadenas arbitrarias no confiables.

Comentarios

Facet soporta comentarios cortos y largos:

{{! short comment }}

{{!--
long comment
spanning multiple lines
--}}

Los comentarios nunca se renderizan en la salida.

Sintaxis de expresiones

Las expresiones son la base de la salida dinámica en plantillas. Permiten acceder a propiedades anidadas, llamar a transformaciones tipo método, aplicar filtros y usar lógica condicional en línea.

Notación de rutas con punto

Acceda a propiedades anidadas con notación de punto:

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

La notación con flecha también se acepta en lugares donde los autores naturalmente escriben acceso a objetos:

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

Use el estilo que sea más claro dentro de la plantilla actual, pero manténgase coherente dentro de un mismo archivo.

Llamadas tipo método

Algunos objetos exponen transformaciones o búsquedas tipo método:

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

Esto es común para:

  • imágenes
  • colecciones
  • URLs localizadas por página
  • helpers de consulta conscientes de la página

Las llamadas tipo método son parte del modelo de objetos de Facet, no ejecución arbitraria de código en tiempo de ejecución.

Parámetros clave-valor

Algunos helpers aceptan parámetros nombrados:

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

Use parámetros nombrados cuando el contrato del helper los espere explícitamente. Esto mantiene las plantillas legibles y evita sobrecargar argumentos posicionales.

Expresiones ternarias

Facet soporta lógica ternaria de salida:

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

Use ternarias para decisiones de salida cortas. Si el branching se vuelve estructural, prefiera un helper de bloque como {{#if}}.

Cadenas de filtros

Use el operador pipe para transformar valores:

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

Los filtros encadenan de izquierda a derecha.

Estilos de argumentos de filtro

Facet acepta tanto el estilo con paréntesis como el estilo con dos puntos para argumentos:

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

Ambas formas son compatibles. Prefiera el estilo con paréntesis en plantillas oficiales porque es más claro y más fácil de revisar.

Helpers de bloque

Los helpers de bloque proveen estructura, no solo salida escalar.

Facet soporta estos tipos principales de bloques:

BloquePropósito
{{#if}}renderizado condicional
{{#each}}iterar sobre arreglos o colecciones existentes
{{#pages}}consultar un conjunto de páginas
{{#page}}consultar una página
{{#languages}}iterar los idiomas disponibles
{{#deferred}}renderizado diferido compatible con caché

{{#if}}

Use {{#if}} para renderizado condicional:

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

Con else:

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

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

También se acepta la variante else if:

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

Operadores de comparación

OperadorSignificadoEjemplo
==igualpage.template == "blog-post"
!=no igualpage.status != 1
>mayor quepage.numChildren > 5
<menor quepage.numChildren < 3
>=mayor o igualpage.sort >= 0
<=menor o igualpage.sort <= 10

Operadores lógicos

OperadorSignificadoEjemplo
&&ANDuser.isLoggedin && user.isSuperuser
||ORpage.template == "blog-post" || page.template == "news"
!NOT!user.isGuest

Reglas de veracidad

La veracidad (truthiness) de Facet sigue un modelo práctico orientado a plantillas:

Valor¿Verdadero?
nullfalso
falsefalso
""falso
0falso
"0"falso
arreglo vacío o colección vacíafalso
todo lo demásverdadero

Si la lógica de la condición empieza a llevar reglas de negocio en lugar de reglas simples de presentación, vuelva al modelo de objetos. No oculte un diseño de contenido débil detrás de bloques if anidados.

{{#each}}

Use {{#each}} cuando la colección ya exista en el contexto actual:

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

Con la abreviatura this:

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

Con else:

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

Variables de contexto de bucle

Dentro de bloques de bucle, estos valores están disponibles:

VariableDescripción
loop.indexíndice basado en cero
loop.index1índice basado en uno
loop.firstverdadero en la primera iteración
loop.lastverdadero en la última iteración
loop.lengthnúmero total de ítems
loop.evenverdadero en índices pares
loop.oddverdadero en índices impares
@indexalias abreviado de loop.index

Ejemplo:

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

this vs alias nombrado

Dentro de {{#each}}, this siempre apunta al ítem actual.

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

Si la legibilidad importa, prefiera un alias explícito:

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

{{#pages}}

Use {{#pages}} cuando la plantilla necesite consultar contenido en lugar de renderizar una colección ya presente en el alcance.

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

Parámetros típicos:

ParámetroDescripción
selectorcadena selector de consulta
asnombre de la variable de bucle

Use {{#pages}} para:

  • archivos
  • páginas de listado
  • widgets de contenido reciente
  • colecciones de navegación
  • bloques de contenido guiados por consultas

Use {{#each}} cuando la colección ya exista. Use {{#pages}} cuando la plantilla deba obtenerla.

{{#page}}

Use {{#page}} cuando solo se deba consultar una página:

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

Los patrones de búsqueda soportados incluyen:

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

Dentro de un bloque {{#page}}, page se refiere a la página consultada durante la duración de ese bloque.

{{#languages}}

Use {{#languages}} para construir interfaces conscientes del idioma, como selectores de idioma:

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

Con un fallback vacío:

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

Propiedades de idioma útiles incluyen:

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

{{#deferred}}

Use {{#deferred}} cuando la página tenga mucha caché pero parte del contenido dependa de datos por usuario o de cambios rápidos en tiempo de ejecución.

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

Con dimensionado personalizado de esqueleto:

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

Úselo para:

  • saludos personalizados conscientes del usuario
  • paneles de cuenta
  • personalización segura para caché
  • otro contenido que no deba fijarse en una respuesta HTML de página completa en caché

deferred debe usarse de forma intencional. No envuelva todo lo dinámico por defecto.

Objetos de contexto

Facet expone un conjunto de objetos de contexto de nivel raíz en cada superficie de renderización soportada.

Los objetos comunes incluyen:

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

Dentro de las plantillas de Componentes, los valores de los campos del Componente también se exponen en el nivel raíz:

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

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

La referencia de objetos en profundidad está documentada en:

Límite de salida de imágenes

La sintaxis de imágenes depende de dónde provenga el objeto de imagen.

Sintaxis de helper de campo image de Component

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

Sintaxis de objeto de imagen de página o consulta

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

Sintaxis de bucle de campo images de Component

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

No mezcle estas formas a la ligera. El campo propietario u objeto en tiempo de ejecución decide qué sintaxis es la correcta.

Salida del helper Map

Los campos tipo mapa pueden exponer salida orientada a helpers:

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

Use esto solo cuando el contrato del campo lo documente explícitamente.

Inserción de Variables

Las Variables se insertan con la sintaxis de doble corchete:

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

Dentro de la plantilla de Variable, los valores pasados están disponibles a través de attr.

Las Variables son para fragmentos reutilizables, no para reemplazar un diseño de objeto adecuado. Si el fragmento crece hasta convertirse en un contrato de sección, vuelva al diseño de Componentes.

Marcadores de embed gestionados

Los formularios y las reseñas normalmente se renderizan mediante marcadores de embed gestionados.

Embed de modelo fijo:

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

Embed respaldado por campo dentro de un Componente reutilizable:

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

Regla de decisión:

  • embed fijo cuando la plantilla posee un flujo de trabajo gestionado estable
  • embed respaldado por campo cuando los autores deben elegir el modelo gestionado en el momento de la autoría

Guía de uso

  • mantenga la lógica de plantillas legible
  • prefiera contratos de campo sólidos sobre trucos inteligentes de plantilla
  • use {{#if}} para estructura, no para ocultar un diseño de contenido débil
  • use {{#each}} cuando la colección ya exista
  • use {{#pages}} cuando la plantilla deba consultar la colección
  • use Variables para pequeños fragmentos reutilizables
  • vuelva al diseño de Component o Lista cuando la lógica de la plantilla crezca demasiado compleja

Errores comunes

Evite estos patrones:

  • usar salida sin escapar donde la salida escapada es más segura
  • mezclar la notación abreviada de campo de Componente y expresiones genéricas de Facet sin entender la diferencia
  • resolver un problema de modelo de contenido solo con condicionales
  • construir una plantilla gigantesca y anidada en lugar de aclarar los límites del objeto
  • repetir el mismo fragmento a través de plantillas en lugar de extraer una Variable
  • usar la sintaxis de helper de campo en campos que no la soportan

Guía rápida de decisiones

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

Continuar