Fragmentos de contenido visual: plantillas visual-content-fragments-templates

En Adobe Experience Manager (AEM) as a Cloud Service, las plantillas de HTML se pueden utilizar para visualizar fragmentos de contenido y entregarlos en formato HTML.

Las plantillas de HTML permiten controlar cómo se muestran los fragmentos de contenido. Puede crear plantillas de HTML en el editor de código que elija, luego cargarlas y asignarlas a modelos de fragmentos de contenido en AEM. Los marcadores de posición de contenido que utilizan Handlebars.js permiten asignar la plantilla a tipos de datos en el modelo de fragmento de contenido. Una vez asignada a un modelo, una plantilla está disponible para utilizarse con cualquier fragmento de contenido basado en el modelo, para visualizar el fragmento o para enviarlo como una experiencia modular en formato HTML a cualquier canal, por ejemplo web, correo electrónico, aplicación móvil u otros.

Este artículo explica cómo crear plantillas de HTML personalizadas con sintaxis de Handlebars para procesar fragmentos de contenido visual.

Después de crear las plantillas, puede:

NOTE
Consulte Fragmentos de contenido visual para cargar, asignar y usar su plantilla en AEM.
NOTE
Use Figma en el trabajo de fragmentos de contenido visual para automatizar la carga de un diseño de HTML.

Lo que aprenderá what-you-will-learn

Después de proporcionar una introducción (muy rápida) a:

  • Uso de las plantillas en AEM
  • Uso de la URL de publicación

Esta página cubre (con más detalle):

  • Handlebars: los conceptos básicos necesarios de la sintaxis
  • Cómo acceder a los datos de fragmentos de contenido
  • Uso de fragmentos de contenido anidados
  • Administrar campos multivalor
  • Creación de bucles y lógica condicional
  • Prácticas recomendadas de diseño de plantillas para fragmentos de contenido

Requisitos previos prerequisites

Para comprender y trabajar con las tecnologías aquí tratadas, debería tener lo siguiente:

  • Comprensión básica de HTML
  • Familiaridad con fragmentos de contenido y modelos de fragmentos de contenido de AEM
  • Comprensión de los modelos de fragmentos de contenido

Uso de una plantilla de HTML de fragmento de contenido using-a-content-fragment-html-template

Uso de una plantilla de HTML de fragmento de contenido en AEM using-a-content-fragment-html-template-in-aem

Para obtener más información sobre cómo utilizar la plantilla en AEM, consulte:

Uso de la URL de publicación de fragmento de contenido visual using-the-visual-content-fragment-publish-url

Una vez que haya creado fragmentos de contenido visual utilizando la plantilla, puede usar la URL de publicación de los fragmentos de contenido visual.

Manillar: los (muy) conceptos básicos handlebars-the-very-basics

Handlebars es un lenguaje de creación de plantillas simple que usa llaves dobles (corchetes) {{ }} para insertar contenido dinámico en HTML.

Sintaxis básica basic-syntax

Un ejemplo de sintaxis básica de Handlebars:

<!-- Output a variable (HTML-escaped) -->
{{snippet-not-found:variableName}}

<!-- Output raw HTML (unescaped) -->
{{{htmlContent}}}

<!-- Comment (not rendered) -->
{{! This is a comment }}

Conceptos clave key-concepts

Los conceptos clave de Handlebars:

Sintaxis
Descripción
Cuándo se usa
{{ }}
Omite los caracteres especiales de HTML
Metadatos, etiquetas, booleanos
{{{ }}}
Genera HTML sin procesar (sin escape)
Salida de recurso y texto enriquecido
{{! }}
Comentario solo de Handlebars
Documentación de plantilla
IMPORTANT
Utilice llaves triples ({{{ }}}) para los valores de campo porque los valores se representan previamente en HTML.

Referencia de contexto de plantilla template-context-reference

Cuando se representa la plantilla, recibe un objeto de contexto que contiene todos los datos sobre el fragmento de contenido. Esto abarcará:

  • el fragmento que ha seleccionado

  • todos los demás fragmentos a los que se hace referencia desde ese fragmento seleccionado

    note
    NOTE
    Se puede hacer referencia a los fragmentos:
    • en la IU: hasta la profundidad máxima de 5
    • al utilizar la API: la profundidad es configurable, hasta la profundidad máxima de 10

Fragmento de contenido principal main-content-fragment

La estructura del objeto de contexto para el fragmento de contenido (seleccionado):

Variable
Tipo
Descripción
properties
Mapa
Metadatos de fragmento (consulte Estructura de propiedades)
fields
Mapa
Acceso directo a valores de campo por nombre
allFields
Lista
Matriz de {name, value} para iteración
hasFields
Booleano
true si el fragmento tiene campos
metadata
Mapa
Bloque de esquema de metadatos del fragmento. Ver también Estructura de metadatos de fragmento de contenido

Estructura de propiedades (Fragmentos principal y de referencia) properties-structure-main-and-referenced-fragments

El objeto properties tiene la misma estructura para el fragmento seleccionado y para cada fragmento referenciado.

Propiedad
Tipo
Descripción
Ejemplo
id
Cadena
UUID del fragmento
title
Cadena
Título del fragmento
Ciclismo en el sur de Utah
description
Cadena
Descripción del fragmento
Una aventura…
path
Cadena
Ruta JCR al fragmento
/content/dam/...
hasDescription
Booleano
True si la descripción no está en blanco
true
createdDate
Cadena
Fecha de creación de ISO-8601
modifiedDate
Cadena
Fecha de modificación ISO-8601
publishedDate
Cadena
Fecha de publicación de ISO-8601
status
Cadena
Estado de replicación para el nivel de publicación
DRAFT
model
Mapa
Contiene: id, path, name, technicalName, description
validationStatus
Lista
Entradas como {property, message}
previewReplicationStatus
Cadena
Estado de replicación para el nivel de previsualización
tags
Lista
Etiquetas de nivel de fragmento. Cada elemento: id, title, titlePath, name, path, description
fieldTags
Lista
Etiquetas de nivel de campo. La misma estructura que tags.

Ejemplos: Acceso a plantillas

Para el fragmento de contenido (seleccionado):

{{properties.title}}, {{properties.description}}, {{{fields.field_name}}}

Fragmentos de contenido referenciados referenced-content-fragments

La estructura del objeto de contexto para cualquier fragmento al que se haga referencia:

Variable
Tipo
Descripción
hasReferencedFragments
Booleano
true cuando existen referencias
referencedFragments
Lista
Matriz de objetos de fragmento referenciados
referencesError
Booleano
true si se produjo un error al cargar las referencias
referencesErrorMessage
Cadena
Mensaje de error cuando referencesError es true

Estructura del fragmento de referencia referenced-fragment-structure

Cada elemento de referencedFragments contiene:

Propiedad
Tipo
Descripción
anchorId
Cadena
ID de anclaje seguro para HTML (en el nivel de fragmento; no es una propiedad de fragmento de contenido)
properties
Mapa
Metadatos de fragmento (la misma estructura que arriba)
hasFields
Booleano
True si el fragmento tiene campos
fields
Mapa
Acceso directo a los campos de este fragmento
allFields
Lista
Matriz de {name, value} para iteración
metadata
Mapa
Bloque de esquema de metadatos del fragmento. Ver también Estructura de metadatos de fragmento de contenido

Ejemplos: Acceso a la plantilla para el primer fragmento de contenido al que se hace referencia (primer elemento de la lista indexada en 0):

{{referencedFragments.[0].anchorId}}, {{referencedFragments.[0].properties.title}}, {{referencedFragments.[0].properties.description}}

O desde el mapa de campos:

{{{ fields.referenced_cf_field_name.properties.description }}}

Estructura de metadatos de fragmento de contenido content-fragment-metadata-structure

Los metadatos de un fragmento (el bloque de metadatos/campos respaldado por su esquema de metadatos) aparecen en metadata. Es distinto de properties, que contiene los valores estructurales (title, description, path, tags, dates, status).

Variable
Tipo
Descripción
metadata
Mapa
Bloque de esquema de metadatos del fragmento. Ausente cuando el fragmento no tiene metadatos, por lo que las expresiones metadata se representan como vacías en lugar de como errores
metadata.schemaId
Cadena
Identificador del formulario de metadatos vinculado al fragmento, resuelto desde el fragmento o sus carpetas principales; default cuando no se encuentra ninguno
metadata.values
Mapa
Propiedades de metadatos personalizadas del fragmento de contenido, con clave por nombre de propiedad. Los valores mantienen su tipo JSON: un número permanece como número, un booleano permanece como booleano
metadata.fields
Lista
Las mismas propiedades que una lista iterable. Cada elemento tiene name, type (cadena, entero, número, booleano, matriz, objeto), value y title (etiqueta legible en lenguaje natural del esquema)
{{metadata.schemaId}}
{{metadata.values.[dc:title]}}        <!-- colon keys need bracket syntax -->
{{metadata.values.version}}
{{#each metadata.fields}}{{title}}: {{value}}{{/each}}

<!-- A referenced fragment carries the same block on its row -->
{{fields.author.metadata.values.[dc:title]}}
{{#each referencedFragments}}{{metadata.schemaId}}{{/each}}
NOTE
Los metadatos del fragmento principal llegan con el fragmento en sí. Los metadatos de un fragmento al que se hace referencia se recuperan por separado, por referencia y solo para las referencias que lee realmente una plantilla.
Dado que metadata.values mantiene los tipos JSON, un número o una lectura booleana de ahí funciona directamente con los ayudantes booleanos y de comparación, a diferencia de un campo escalar que alcanza la plantilla preprocesada como una cadena. Consulte también Comparación y ayudantes de lógica booleana.

Estructura del campo de recursos asset-field-structure

Los recursos a los que se hace referencia no son una colección de nivel superior; no hay referencedAssets. Se llega a un recurso a través del campo que lo contiene, fields.<assetField> o fields.<ref>.fields.<assetField> para un recurso dentro de un fragmento al que se hace referencia.

Un valor de campo de recurso tiene doble naturaleza. Impreso sin procesar es el recurso procesado previamente por HTML; si se profundiza en, expone los metadatos del recurso en properties.

Variable
Tipo
Descripción
el valor en sí
Cadena (HTML)
El recurso procesado previamente HTML (<img> para una imagen).
Requiere llaves triples: {{{fields.heroImage}}}
properties
Mapa
Propiedades de metadatos del recurso

Un campo de recursos de varios valores es una lista. Itere con {{#each fields.gallery}}, usando {{{this}}} para HTML y {{this.properties.assetId}} para los metadatos.

Propiedades de un recurso DAM local properties-of-a-local-dam-asset

Estas claves las lleva la respuesta de fragmento de contenido y siempre están disponibles:

Propiedad
Tipo
Descripción
Ejemplo
assetId
Cadena
Identificador de recurso como URN
urn:aaid:aem:1fb05fe4-...
path
Cadena
Ruta DAM del recurso
/content/dam/wknd/ian_provo.jpg
name
Cadena
Nombre de archivo
ian_provo.jpg
title
Cadena
Título del recurso
description
Cadena
Descripción del recurso
type
Cadena
Tipo de referencia; siempre asset
asset
fieldName
Cadena
Nombre del campo Fragmento de contenido que contiene la referencia
profilePicture
status
Cadena
NEW, DRAFT, PUBLISHED, MODIFIED, UNPUBLISHED
DRAFT
previewReplicationStatus
Cadena
PUBLISHED, UNPUBLISHED, MODIFIED, NEVER_PUBLISHED
created, modified, published
Mapa
Información de creación: at (ISO-8601), by, fullName, firstName, lastName
dc:format
Cadena
Tipo de mime
image/jpeg
repo:size
Número
Tamaño en bytes
251434
tiff:ImageWidth
Número
Anchura en píxeles
1152
tiff:ImageHeight
Número
Altura en píxeles
1152
cualquier otra propiedad de metadatos
mecanografiado
Todas las demás propiedades del recurso, incluidas las que tienen espacios de nombres personalizados como yournamespace:persistentID. Obtenido del recurso bajo demanda

Los nombres de propiedad que contienen dos puntos requieren la sintaxis entre corchetes:

{{{fields.heroImage}}}
{{fields.heroImage.properties.assetId}}
{{fields.heroImage.properties.[dc:format]}}
{{fields.heroImage.properties.[yournamespace:persistentID]}}

Propiedades de un recurso de Dynamic Media properties-of-a-dynamic-media-asset

Un recurso de Dynamic Media (remoto) no lleva ninguna ruta DAM. Solo hay dos claves en línea:

Propiedad
Tipo
Descripción
Ejemplo
repository
Cadena
Host de envío que sirve al recurso
delivery-p12345-e67890.adobeaemcloud.com
assetId
Cadena
Identificador de recurso como URN
urn:aaid:aem:1fb05fe4-...

Juntos, esto es suficiente para crear una URL de entrega de Dynamic Media en lugar de utilizar la imagen incrustada. Las demás propiedades se recuperan del host de entrega del recurso. Solo se sirven allí los recursos aprobados (publicados), por lo que un recurso de Dynamic Media no aprobado no resuelve nada más allá de estas dos claves.

NOTE
Dentro de un bucle, escriba this.properties.<key> y no un properties.<key> vacío. Una ruta de acceso vacía cuya clave también es una propiedad de fragmento de contenido, como title, description, path o status, se resuelve en el fragmento y no en el recurso.

Acceso básico al campo basic-field-access

Se recomienda el acceso directo al campo, si es necesario, puede iterar en todos los campos.

Acceda a los campos directamente por nombre mediante el mapa de campos:

<!DOCTYPE html>
<html>
<head>
  <title>{{properties.title}}</title>
</head>
<body>
  <article>
    <h1>{{{fields.title}}}</h1>
    <p class="subtitle">{{{fields.subtitle}}}</p>
    <div class="content">
      {{{fields.description}}}
    </div>
    <div class="image">
      {{{fields.primaryImage}}}
    </div>
  </article>
</body>
</html>

Recuerde:

  • Utilice llaves triples {{{ }}} para los valores de campo si contienen HTML procesado previamente (texto enriquecido)
  • Los nombres de campo (título, subtítulo, descripción, primaryImage) deben coincidir con el modelo de fragmento de contenido exactamente
  • Los campos que faltan no se representan: no se generan errores y la sintaxis de Handlebars permanece presente (y visible) en el fragmento de HTML procesado

Iterar en todos los campos iterate-through-all-fields

Utilice allFields cuando no conozca los nombres de campo por adelantado:

<table>
  <thead>
    <tr>
      <th>Field Name</th>
      <th>Field Value</th>
    </tr>
  </thead>
  <tbody>
    {{#each allFields}}
    <tr>
      <td>{{name}}</td>
      <td>{{{value}}}</td>
    </tr>
    {{/each}}
  </tbody>
</table>

Recuerde:

  • {{name}} usa llaves dobles (etiqueta de texto sin formato)
  • {{{value}}} usa llaves triples (valor de HTML procesado previamente)

Fragmentos de contenido anidados nested-content-fragments

Cuando un campo de fragmento de contenido hace referencia a otro fragmento de contenido, puede utilizar la notación de puntos para acceder directamente a los campos del fragmento al que se hace referencia.

Anidado de un solo nivel single-level-nesting

Un ejemplo de anidamiento de un solo nivel:

<article>
  <h1>{{{fields.title}}}</h1>

  <!-- Access author (a referenced Content Fragment) -->
  <div class="author-info">
    <h3>Author</h3>
    <p>Name: {{{fields.author.name}}}</p>
    <p>Email: {{{fields.author.email}}}</p>
    <p>Bio: {{{fields.author.bio}}}</p>
  </div>

  <div class="content">
    {{{fields.content}}}
  </div>
</article>

Patrón: fields.referenceFieldName.nestedFieldName

Anidado de varios niveles multi-level-nesting

El sistema admite una profundidad de anidación ilimitada:

<article>
  <h1>{{{fields.title}}}</h1>

  <div class="author-details">
    <!-- Level 1: Author -->
    <p>Author: {{{fields.author.name}}}</p>

    <!-- Level 2: Author's Organization -->
    <p>Organization: {{{fields.author.organization.name}}}</p>
    <p>Website: {{{fields.author.organization.website}}}</p>

    <!-- Level 3: Organization's Address -->
    <p>Located in: {{{fields.author.organization.address.city}}},
    {{{fields.author.organization.address.country}}}</p>
  </div>

  <div class="content">
    {{{fields.content}}}
  </div>
</article>

Patrón: fields.level1.level2.level3.fieldName (profundidad limitada; el valor predeterminado es 5, se puede ampliar a 10 cuando se utiliza la API)

Requisito de parámetro de API: hidratación api-parameter-requirements

Para habilitar el acceso anidado a fragmentos de contenido, debe incluir el parámetro de consulta hydration en la llamada de API:

Para habilitar la hidratación:

# Enable hydration with depth=2 for 2 levels of nesting
GET /adobe/sites/cf/fragments/{id}/preview?hydration=%7B%22enabled%22%3Atrue%2C%22maxDepth%22%3A2%7D
maxDepth
Qué se carga
1
Fragmento principal + referencias directas
2
Fragmento principal + referencias directas + sus referencias
3+
Continuar hasta 10 niveles

Campos multivalor multi-valued-fields

Existen varios tipos de campos multivalorados.

Campos de texto de varios valores multi-valued-text-fields

Los campos de texto, number, fecha y otros campos simples se convierten en matrices cuando se asignan varios valores:

<article>
  <h1>{{{fields.title}}}</h1>

  <!-- Access individual items by index (use dot before bracket) -->
  <div class="tags">
    <span class="tag">{{{fields.tags.[0]}}}</span>
    <span class="tag">{{{fields.tags.[1]}}}</span>
  </div>

  <!-- Better: Iterate through all tags -->
  <div class="tags">
    {{#each fields.tags}}
    <span class="tag">{{{this}}}</span>
    {{/each}}
  </div>
</article>

Recuerde, cuando acceda a elementos de matriz por índice en Handlebars:

  • Use:
    • .[0] (punto antes del corchete)
  • No:
    • [0]

Campos numéricos multivalorados multi-valued-number-fields

Los números se convierten en cadenas para su procesamiento:

<div class="pricing">
  <h3>Available Prices:</h3>
  {{#each fields.prices}}
  <span class="price">${{{this}}}</span>
  {{/each}}
</div>

Referencias de fragmentos de contenido multivalor multi-valued-content-fragment-references

Cuando un campo hace referencia a varios fragmentos de contenido:

<div class="authors">
  <h3>Authors:</h3>
  {{#each fields.authors}}
  <div class="author">
    <h4>{{{this.name}}}</h4>
    <p>Email: {{{this.email}}}</p>
    {{#if this.bio}}
    <p class="bio">{{{this.bio}}}</p>
    {{/if}}
  </div>
  {{/each}}
</div>

Referencias de recursos de varios valores multi-valued-asset-references

Referencia de contenido campos configurados para tipos de contenido que son recursos (por ejemplo, imágenes y documentos) se representan previamente como HTML. Los recursos de varios valores se convierten en matrices:

<!-- Single asset -->
<div class="hero-image">
  {{{fields.heroImage}}}
</div>

<!-- Multi-valued: iterate through all images -->
<div class="gallery">
  {{#each fields.gallery}}
  <div class="image">{{{this}}}</div>
  {{/each}}
</div>

Referencias anidadas de varios valores nested-multi-valued-references

Las referencias de varios valores pueden contener referencias de varios valores en cualquier profundidad:

{{#each fields.chapters}}
<div class="chapter">
  <h3>Chapter: {{{this.title}}}</h3>

  {{#each this.authors}}
  <p>Author: {{{this.name}}}</p>

  {{#each this.publications}}
  <p>Publication: {{{this.title}}}</p>
  {{/each}}
  {{/each}}
</div>
{{/each}}

Bucles e iteración loops-and-iteration

Handlebars proporciona el ayudante {{#each}} para recorrer en iteración matrices y objetos.

Iteración en matrices iterating-over-arrays

Un ejemplo de iteración en matrices:

<!-- Simple array iteration -->
{{#each fields.tags}}
<span class="tag">{{{this}}}</span>
{{/each}}

<!-- Array of objects -->
{{#each fields.authors}}
<div class="author">
  <h4>{{{this.name}}}</h4>
  <p>{{{this.email}}}</p>
</div>
{{/each}}

<!-- With empty-state fallback -->
{{#each fields.tags}}
<span class="tag">{{{this}}}</span>
{{snippet-not-found:else}}
<p>No tags available.</p>
{{/each}}

Variables especiales en bucles special-variables-in-loops

Dentro de bloques de {{#each}}, Handlebars proporciona variables especiales:

{{#each fields.items}}
<div class="item">
  <p>Index: {{@index}}</p>     <!-- 0-based index -->
  <p>Number: {{@number}}</p>   <!-- 1-based index -->
  <p>First: {{@first}}</p>     <!-- true for first item -->
  <p>Last: {{@last}}</p>       <!-- true for last item -->
  <p>Value: {{{this}}}</p>     <!-- current item -->
</div>
{{/each}}

<!-- Example: numbered steps with first/last CSS classes -->
<ul>
  {{#each fields.steps}}
  <li class="{{#if @first}}first{{/if}} {{#if @last}}last{{/if}}">
    Step {{@number}}: {{{this}}}
  </li>
  {{/each}}
</ul>

Iteración en fragmentos referenciados iterating-over-referenced-fragments

Ejemplo de iteración en fragmentos referenciados:

{{#if hasReferencedFragments}}
<section class="references">
  <h2>Related Content</h2>
  {{#each referencedFragments}}
  <article id="{{anchorId}}">
    <h3>{{title}}</h3>
    {{#if hasDescription}}
    <p>{{description}}</p>
    {{/if}}
    {{#if hasFields}}
    <ul>
      {{#each allFields}}
      <li><strong>{{name}}:</strong> {{{value}}}</li>
      {{/each}}
    </ul>
    {{/if}}
  </article>
  {{/each}}
</section>
{{/if}}

Bucles anidados nested-loops

Un ejemplo de bucles anidados:

{{#each fields.categories}}
<section class="category">
  <h2>{{{this.name}}}</h2>

  {{#each this.products}}
  <article class="product">
    <h3>{{{this.name}}}</h3>
    <p>{{{this.description}}}</p>
  </article>
  {{/each}}
</section>
{{/each}}

Renderización condicional conditional-rendering

Utilice condicionales para mostrar u ocultar contenido en función de la disponibilidad de los datos.

If/Else básico basic-if-else

Un ejemplo de una construcción básica if-else:

{{#if hasMainDescription}}
<p class="description">{{properties.description}}</p>
{{snippet-not-found:else}}
<p class="no-description">No description available.</p>
{{/if}}

<!-- Check field existence before rendering -->
{{#if fields.author}}
<div class="author">
  <p>By {{{fields.author.name}}}</p>
</div>
{{/if}}

{{#if fields.publishDate}}
<time>{{{fields.publishDate}}}</time>
{{/if}}

A menos que (negativo o condicional) unless-negative-conditional

Un asistente de unless:

<!-- Show author unless explicitly hidden -->
{{#unless fields.hideAuthor}}
<div class="author">{{{fields.author.name}}}</div>
{{/unless}}
NOTE
Campos booleanos:
Esto se lee correctamente cuando hideAuthor es un valor booleano real; por ejemplo, de metadata.values.*. Sin embargo, un campo booleano de un solo valor, llega preprocesado como la cadena "true" o "false", y "false" es una cadena que no está vacía (verdadera), por lo que {{#unless fields.hideAuthor}} oculta al autor incluso cuando el campo es false. Compare el campo escalar explícitamente en su lugar; por ejemplo, {{#if (eq fields.hideAuthor "true")}}. Consulte también Comparación y ayudantes de lógica booleana.

Condiciones anidadas nested-conditials

Un ejemplo de condicional anidado:

{{#if fields.author}}
<div class="author">
  <h3>{{{fields.author.name}}}</h3>

  {{#if fields.author.bio}}
  <p class="bio">{{{fields.author.bio}}}</p>
  {{/if}}

  {{#if fields.author.website}}
  <a href="{{{fields.author.website}}}">Visit Website</a>
  {{/if}}
</div>
{{/if}}

Comparación y ayudantes de lógica booleana comparison-and-boolean-logic-helpers

Además de los ayudantes de stock {{#if}} y {{#unless}}, el servicio registra los ayudantes de comparación y de lógica booleana para que una plantilla pueda procesarse condicionalmente en los valores de campo. Tanto los ayudantes de comparación como los de lógica booleana funcionan como un bloque y como una subexpresión en línea:

{{#eq fields.tier "gold"}}Gold{{else}}Standard{{/eq}}     equality (null-safe, type-aware)
{{#neq fields.status "draft"}}Published{{/neq}}           inequality
{{#gte metadata.values.quantity 5}}Lots{{/gte}}           ordering: gt, gte, lt, lte
{{#and a b}}both{{/and}} {{#or a b}}either{{/or}} {{#not flag}}off{{/not}}   boolean logic
{{#if (and (gt n 0) (lt n 10))}}in range{{/if}}           composed as sub-expressions
{{gt n 5 yes="big" no="small"}}                           inline, with yes/no substitution

Comparación numérica en campos de un solo valor numeric-comparison-on-single-valued-fields

Un campo de un solo valor se presenta como una cadena (fields.quantity is "7", no 7), pero los ayudantes hacen que una cadena numérica sea obligatoria en el momento de la comparación, por lo que {{#gte fields.quantity 5}} y {{#eq fields.quantity 0}} se comparan como números, junto con {{#gte metadata.values.quantity 5}}, donde el valor ya se ha escrito. La ordenación (gt/gte/lt/lte) siempre se compara numéricamente. Coerces de igualdad con intención: un campo de cadena se compara como un número sólo cuando el otro operando es un número auténtico: un literal numérico ({{#eq fields.qty 0}}) o un campo escrito como metadata.values.*. La comparación de dos campos de cadena o un literal entre comillas ({{#eq fields.version "1.0"}}) mantiene la igualdad de cadena exacta, por lo que las versiones, los códigos postales y los identificadores modelados como texto nunca se contraen ("007" no es igual a "7"). Las fechas se cruzan como cadenas ISO-8601, por lo que eq/neq las comparan por igualdad de cadena exacta y los ayudantes de pedidos las rechazan como no numéricas. Un operando que falta, en blanco o no numérico se trata como “condición no cumplida” y procesa la rama else en lugar de generar errores.

Los ayudantes booleanos utilizan la veracidad simple (sin coerción) boolean-helpers-use-plain-truthiness-no-coercion

Los ayudantes booleanos and/or/not no hacen la coerción: aplican la veracidad de Handlebars estándar, donde cualquier cadena que no esté vacía es verdadera (los valores falsos son false, null, undefined, 0, "", []). Un campo booleano de un solo valor se presenta como la cadena "true" o "false", y "false" es una cadena que no está vacía, por lo que {{#not fields.flag}} ve un valor “true” y toma la rama else incluso cuando el campo indica “false”. Compare explícitamente un campo booleano escalar ({{#eq fields.flag "true"}}) en lugar de proporcionarlo a and/or/not; ya se ha escrito una lectura booleana de metadata.values.* y funciona directamente con los ayudantes booleanos.

Campos con nombre de asistente fields-named-after-a-helper

Si se registran estos ayudantes, sus nombres se reclaman globalmente, pero un campo con el nombre de uno sigue procesando su propio valor cuando se lee sin argumentos: {{eq}} dentro de {{#with fields}} imprime el texto del campo eq, no un resultado de comparación. Los ayudantes están disponibles tanto en la plantilla de visualización como dentro de Handlebars incrustados en un valor de campo de fragmento de contenido.

Ayudantes de Handlebars integrados built-in-handlebars-helpers

Handlebars incluye varios ayudantes integrados, más allá de {{#if}} y {{#each}}.

Ayudante
Descripción
{{#if condition}}
Procesa el contenido si la condición es verdadera. Valores de False: false, undefined, null, 0, "", []
{{#unless condition}}
Procesa contenido si la condición es falsa (al contrario de #if)
{{#each array}}
Repite el contenido de cada elemento; admite {{else}} para matrices vacías
{{#with object}}
Crea un nuevo ámbito para un objeto anidado, lo que reduce la repetición de rutas
{{lookup this "key"}}
Busca dinámicamente una propiedad por su nombre

Con el asistente with-helper

Crea un nuevo ámbito para objetos anidados para reducir los prefijos de ruta repetitiva:

{{#with fields.author}}
<div class="author">
  <h3>{{{name}}}</h3>     <!-- same as fields.author.name -->
  <p>{{{email}}}</p>      <!-- same as fields.author.email -->
  <p>{{{bio}}}</p>        <!-- same as fields.author.bio -->
</div>
{{/with}}

<!-- Useful for deeply nested objects -->
{{#with fields.author.organization}}
<div class="organization">
  <h4>{{{name}}}</h4>
  <p>{{{website}}}</p>
  {{#with address}}
  <address>
    {{{street}}}<br/>
    {{{city}}}, {{{country}}}
  </address>
  {{/with}}
</div>
{{/with}}

Avanzar patrones advanced-patterns

A continuación se muestran algunos ejemplos de patrones avanzados.

Acceso al contexto principal en bucles anidados accessing-parent-context-in-nested-loops

Use ../ para acceder al ámbito principal desde un bucle anidado:

<h1>{{{fields.title}}}</h1>

{{#each fields.chapters}}
<section class="chapter">
  <h2>Chapter {{@number}}: {{{this.title}}}</h2>

  {{#each this.sections}}
  <article>
    <!-- Access parent chapter via ../ -->
    <p>Chapter: {{{../title}}}</p>

    <!-- Access root context via ../../ -->
    <p>Book: {{{../../fields.title}}}</p>

    <h3>{{{this.title}}}</h3>
    <div>{{{this.content}}}</div>
  </article>
  {{/each}}
</section>
{{/each}}

Clases CSS dinámicas dynamic-css-classes

Un ejemplo de clases CSS dinámicas:

<article class="content-fragment {{#if hasMainDescription}}with-description{{/if}} {{#if hasReferencedFragments}}has-refs{{/if}}">
  <h1>{{properties.title}}</h1>
</article>

<ul class="tag-list">
  {{#each fields.tags}}
  <li class="tag {{#if @first}}first{{/if}} {{#if @last}}last{{/if}}">
    {{{this}}}
  </li>
  {{/each}}
</ul>

Ejemplos completos complete-examples

Se proporcionan varios ejemplos completos como referencia.

Publicación de blog con autor

Una publicación de blog con detalles del autor:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{properties.title}}</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 40px; }
    .author-card { background: #f5f5f5; padding: 20px; border-radius: 8px; }
    .tags { display: flex; gap: 10px; }
    .tag { background: #007bff; color: white; padding: 5px 10px; border-radius: 4px; }
  </style>
</head>
<body>
  <article>
    <header>
      <h1>{{{fields.title}}}</h1>
      {{#if fields.publishDate}}
      <time datetime="{{{fields.publishDate}}}">{{{fields.publishDate}}}</time>
      {{/if}}
      {{#if fields.tags}}
      <div class="tags">
        {{#each fields.tags}}
        <span class="tag">{{{this}}}</span>
        {{/each}}
      </div>
      {{/if}}
    </header>

    {{#if fields.heroImage}}
    <figure>
      {{{fields.heroImage}}}
      {{#if fields.imageCaption}}
      <figcaption>{{{fields.imageCaption}}}</figcaption>
      {{/if}}
    </figure>
    {{/if}}

    <div class="content">
      {{{fields.content}}}
    </div>

    {{#if fields.author}}
    <aside class="author-card">
      <h3>About the Author</h3>
      <h4>{{{fields.author.name}}}</h4>
      {{#if fields.author.profilePicture}}
      <div class="author-image">{{{fields.author.profilePicture}}}</div>
      {{/if}}
      {{#if fields.author.bio}}
      <p>{{{fields.author.bio}}}</p>
      {{/if}}
      {{#if fields.author.email}}
      <p>Contact: <a href="mailto:{{{fields.author.email}}}">{{{fields.author.email}}}</a></p>
      {{/if}}
    </aside>
    {{/if}}
  </article>
</body>
</html>

Llamada de API requerida:

GET /adobe/sites/cf/fragments/{id}/preview?hydration=%7B%22enabled%22%3Atrue%2C%22maxDepth%22%3A1%7D

Vista de tabla genérica (sin conocimientos previos de campos) generic-table-view-no-prior-knowledge-of-fields

Una vista de tabla genérica, sin un conocimiento inherente de campos. es similar a la Plantilla genérica:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{{properties.title}}</title>
  <style>
    body { font-family: Arial, sans-serif; margin: 40px; }
    table { width: 100%; border-collapse: collapse; margin: 20px 0; }
    th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
    th { background-color: #f4f4f4; font-weight: bold; }
    .ref-section { background: #f9f9f9; padding: 20px; margin: 20px 0; border-radius: 8px; }
  </style>
</head>
<body>
  <header>
    <h1>{{properties.title}}</h1>
    {{#if properties.description}}<p>{{properties.description}}</p>{{/if}}
    <p><small>Path: {{properties.path}}</small></p>
  </header>

  {{#if hasFields}}
  <section>
    <h2>Fields</h2>
    <table>
      <thead>
        <tr><th>Field Name</th><th>Field Value</th></tr>
      </thead>
      <tbody>
        {{#each allFields}}
        <tr>
          <td><strong>{{name}}</strong></td>
          <td>{{{value}}}</td>
        </tr>
        {{/each}}
      </tbody>
    </table>
  </section>
  {{/if}}

  {{#if hasReferencedFragments}}
  <section class="ref-section">
    <h2>Referenced Content Fragments</h2>
    {{#each referencedFragments}}
    <article id="{{anchorId}}" style="margin-bottom: 30px;">
      <h3>{{title}}</h3>
      {{#if hasDescription}}<p>{{description}}</p>{{/if}}
      <p><small>Path: {{path}}</small></p>
      {{#if hasFields}}
      <table>
        <thead>
          <tr><th>Field Name</th><th>Field Value</th></tr>
        </thead>
        <tbody>
          {{#each allFields}}
          <tr>
            <td><strong>{{name}}</strong></td>
            <td>{{{value}}}</td>
          </tr>
          {{/each}}
        </tbody>
      </table>
      {{/if}}
    </article>
    {{/each}}
  </section>
  {{/if}}
</body>
</html>

Prácticas recomendadas best-practices

Las prácticas recomendadas incluyen:

  1. Utilice siempre llaves triples para los valores de campo que contengan contenido de marcado de HTML.

    • Los valores de campo se representan previamente en HTML.

      note
      NOTE
      Las llaves dobles muestran las etiquetas HTML sin procesar como texto sin formato.
    code language-handlebars
    <!-- CORRECT -->
    {{{fields.description}}}
    
    <!-- WRONG - displays HTML tags as text -->
    {{fields.description}}
    
  2. Compruebe la existencia antes de acceder a los campos anidados.

    code language-handlebars
    <!-- GOOD: check before accessing nested fields -->
    {{#if fields.author}}
    <p>By {{{fields.author.name}}}</p>
    {{/if}}
    
    <!-- RISKY: may render empty if author is not set -->
    <p>By {{{fields.author.name}}}</p>
    
  3. Utilice el acceso directo al campo siempre que sea posible.

    • Es más fácil de leer y de mantener que repetir allFields y coincidir por nombre.
  4. Estructurar plantillas con comentarios de sección.

    code language-handlebars
    {{! ===== HEADER SECTION ===== }}
    <header>
      <h1>{{properties.title}}</h1>
    </header>
    
    {{! ===== MAIN CONTENT ===== }}
    <main>
      {{#if hasFields}}
      <!-- fields rendering -->
      {{/if}}
    </main>
    
    {{! ===== REFERENCES ===== }}
    {{#if hasReferencedFragments}}
    <!-- references rendering -->
    {{/if}}
    
  5. Gestionar correctamente los datos que faltan con reserva.

    code language-handlebars
    {{#if fields.title}}
    <h1>{{{fields.title}}}</h1>
    {{snippet-not-found:else}}
    <h1>Untitled</h1>
    {{/if}}
    
  6. Utilice siempre una estructura de documentos de HTML adecuada.

    code language-handlebars
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>{{properties.title}}</title>
    </head>
    <body>
      <!-- your content here -->
    </body>
    </html>
    
  7. Realice pruebas con una variedad de escenarios de contenido:

    • Todos los campos completados
    • Faltan campos opcionales
    • Campos multivalor vacíos
    • Anidado profundo (varios niveles)
    • Referencias que no se cargan
  8. Utilice elementos semánticos de HTML:

    • Para una mejor accesibilidad, use <article>, <header>, <main>, <footer>, <time>, <address> o similar.
  9. Mantener estilos en CSS.

    • Utilice etiquetas <style> u hojas de estilo externas.
    • Evite los estilos en línea siempre que sea posible.
  10. Lógica compleja del documento:

    • Use los comentarios de Handlebars ({{! }}).
    • No utilice comentarios de HTML, que aparecen en la salida procesada.

Resolución de problemas troubleshooting

Algunas sugerencias para la solución de problemas incluyen:

Problema
Síntoma
Solución
El campo muestra las etiquetas HTML como texto
<p>Hello World</p> se muestra literalmente
Usar llaves triples: {{{fields.description}}}
Los campos de fragmento de contenido anidado están vacíos o muestran [objeto Objeto]
{{{fields.author.name}}} está en blanco
Habilite la hidratación en la llamada de API; compruebe la ortografía del nombre del campo; compruebe que maxDepth tenga la profundidad suficiente
El campo multivalor solo muestra el primer elemento
La matriz con cinco elementos solo procesa uno
Usar {{#each fields.tags}} para repetir todos los elementos
El acceso al índice de matriz no funciona
{{{fields.tags[0]}}} se representa vacío
Usar sintaxis con corchetes: {{{fields.tags.[0]}}}
Los fragmentos referenciados no aparecen
hasReferencedFragments siempre es falso
Habilitar hidratación: ?hydration=%7B%22enabled%22%3Atrue%7D; también comprobar {{#if referencesError}}
La plantilla no procesa nada
Página vacía o salida en blanco
Compruebe si hay {{#if}} o {{#each}} bloques sin cerrar; agregue la salida de diagnóstico: <pre>hasFields: {{hasFields}} | title: {{properties.title}}</pre>
Los comentarios aparecen en la página representada
Texto de comentario de HTML visible para los usuarios finales
Use los comentarios de Handlebars {{! comment }} en lugar de HTML <!-- comment -->
El condicional siempre se evalúa como verdadero
{{#if fields.enabled}} siempre es verdadero
Nota: la cadena "false" es verdadera en Handlebars. Solo los datos reales false, null, undefined, 0, "" y [] son falsos.
Caracteres especiales representados como entidades
&lt;, &amp; mostrado en lugar de <, &
Utilice llaves triples para el contenido de HTML procesado previamente: {{{fields.content}}}
No se puede acceder a la variable de bucle externo desde el bucle interno
La variable del elemento principal #each no está definida
Usar ../ para el ámbito principal: {{{../name}}}; usar ../../ para el ámbito principal
La lista vacía no muestra el mensaje de reserva
El campo multivalor con cero elementos no muestra nada
Usar {{else}} dentro de {{#each}}: {{#each fields.tags}}...{{else}}<p>No tags</p>{{/each}}

Uso de recursos working-with-assets

AEM procesa previamente como HTML los fragmentos de contenido a los que se hace referencia en Assets. Por lo tanto, las llaves triples son obligatorias para todas las referencias de recursos.

Tipo de recurso
Procesado como
Imágenes
<img src="..." alt="...">
Vídeos
<video> elemento
Documentos
<a href="..."> vínculo

Recuerde:

  • Utilice siempre llaves triples para los campos de recursos.
    Si utiliza llaves dobles, se omitirá la etiqueta de HTML generada y se mostrará como texto sin procesar en lugar de representar la imagen, el vídeo o el vínculo.

Uso del campo de recursos asset-field-usage

Un ejemplo de uso de campos de recursos:

<!-- CORRECT - triple braces render the image -->
{{{fields.heroImage}}}
<!-- Output: <img src="path/to/image.jpg" alt="Hero"> -->

<!-- WRONG - double braces escape the tag, showing it as text -->
{{fields.heroImage}}
<!-- Output: &lt;img src="path/to/image.jpg" alt="Hero"&gt; -->

Ayudantes de plantilla personalizados customer-template-helpers

El sistema proporciona controladores Handlebars personalizados para generar elementos HTML con atributos HTML personalizados. Estos ayudantes le proporcionan control sobre el marcado generado, a la vez que gestionan la complejidad de extraer direcciones URL de origen a partir de contenido procesado previamente.

Ayudantes disponibles:

  1. asset - Genera etiquetas <img> con atributos personalizados
  2. text: genera <span> etiquetas que ajustan el contenido de texto con atributos personalizados

asset asistente asset-helper

Sintaxis:

{{{asset fieldValue attribute1="value1" attribute2="value2"}}}

Recuerde:

  • Utilice llaves triples {{{ }}} con el asistente de recursos, no llaves dobles.

Cuatro ejemplos básicos four-basic-examples

Cuatro ejemplos básicos son:

<!-- Add a CSS class to an image -->
{{{asset fields.heroImage class="hero-image"}}}
<!-- Output: <img src="..." alt="..." class="hero-image"> -->

<!-- Add multiple CSS classes -->
{{{asset fields.productImage class="product-img responsive shadow"}}}

<!-- Add id and class -->
{{{asset fields.logo class="brand-logo" id="main-logo"}}}

<!-- Add data attributes -->
{{{asset fields.thumbnail class="thumb" data-category="product" data-id="123"}}}

Atributos admitidos supported-attributes

Puede añadir cualquier atributo de HTML válido:

Atributo
Ejemplo
class
class="my-class another-class"
id
id="unique-id"
alt
alt="Custom alt text" (overrides existing alt)
data-*
data-index="1" data-type="hero"
aria-*
aria-label="Description" aria-hidden="true"
width
width="300"
height
height="200"
loading
loading="lazy"
style
style="border-radius: 8px;"

Omitir texto alternativo override-alt-text

Se puede anular el atributo alt de la imagen original:

{{{asset fields.photo alt="Custom description for accessibility"}}}

Ejemplo complejo complex-example

Un ejemplo complejo es:

<article class="blog-post">
<header>
{{{asset fields.featuredImage
class="featured-image responsive"
id="post-hero"
loading="lazy"
data-post-id="12345"}}}
</header>
</article>

Uso de con bucles using-with-loops

Asistente de recursos en bucles:

{{#each fields.galleryImages}}
{{{asset this class="gallery-item" data-index=@index}}}
{{/each}}

text asistente text-helper

El asistente de texto genera una etiqueta <span> que ajusta el contenido de texto con clases CSS y atributos HTML personalizados. Útil para aplicar estilo a campos de texto individuales.

Sintaxis:

{{{text fieldValue attribute1="value1" attribute2="value2"}}}

Recuerde:

  • Utilice llaves triples {{{ }}} con el asistente de texto, no llaves dobles.

Tres ejemplos básicos three-basic-examples

Tres ejemplos básicos son:

<!-- Add a CSS class to text -->
{{{text fields.title class="article-title"}}}
<!-- Output: <span class="article-title">The Title Text</span> -->

<!-- Add multiple attributes -->
{{{text fields.price class="price-tag" id="product-price" data-currency="USD"}}}

<!-- Style inline text -->
{{{text fields.highlightedText class="highlighted" style="background: yellow;"}}}

Casos de uso comunes common-use-cases

Algunos casos de uso comunes incluyen:

<!-- Styling article metadata -->
<article>
<header>
{{{text fields.category class="category-badge"}}}
<h1>{{{fields.title}}}</h1>
{{{text fields.author class="byline"}}}
{{{text fields.publishDate class="date"}}}
</header>
</article>

<!-- Creating styled labels -->
<div class="product-card">
{{{text fields.productName class="product-name"}}}
{{{text fields.brand class="brand-label" data-brand-id="abc"}}}
{{{text fields.price class="price" id="main-price"}}}
</div>

<!-- Accessibility enhancements -->
{{{text fields.importantNote class="alert" role="alert" aria-live="polite"}}}

Con bucles with-loops

Un caso de uso común con bucles incluye:

{{#each fields.tags}}
{{{text this class="tag-badge"}}}
{{/each}}

Ayudantes: validación de atributos helpers-attribute-validation

Ambos ayudantes validan los nombres de atributo antes de incluirlos en la salida.

Nombres de atributo válidos:

  • Debe comenzar por una letra (a-z, A-Z)

  • Solo puede contener letras, dígitos, guiones y guiones bajos; consulte las Convenciones de nombres

  • Sin distinción de mayúsculas y minúsculas

  • Por ejemplo:

    • Válido:
      • class, id, data-value, aria-label, my_attr, dataIndex1
    • No válido:
      • 123-attr, -class, @special, $money

Los nombres de atributo no válidos se omiten silenciosamente con una advertencia en los registros:

{{{asset fields.image class="valid" 123-invalid="skipped" id="also-valid"}}}
<!-- Output: <img src="..." alt="..." class="valid" id="also-valid"> -->
<!-- 123-invalid is skipped because it starts with a number -->

Recuerde:

  • Compruebe los registros del servidor para ver las advertencias “Formato de nombre de atributo no válido bloqueado”.

Comparación de la salida directa con los ayudantes comparing-direct-output-to-helpers

Cuándo se debe establecer la salida directa {{{fields.xxx}}}:

  • No necesita un estilo personalizado
  • Desea el resultado predeterminado tal cual
  • El campo contiene HTML complejo que no desea modificar

Cuándo usar los ayudantes:

  • Debe añadir clases CSS para el estilo
  • Debe agregar atributos personalizados de HTML (data-*, aria-* y otros)
  • Desea una estructura de HTML coherente y controlada

Comparación:

<!-- Direct output - uses whatever HTML the system generates -->
{{{fields.heroImage}}}
<!-- Output: <img src="/path/image.jpg" alt="Hero Image"> -->

<!-- With asset helper - full control over attributes -->
{{{asset fields.heroImage class="hero responsive" id="main-hero" loading="lazy"}}}
<!-- Output: <img src="/path/image.jpg" alt="Hero Image" class="hero responsive" id="main-hero" loading="lazy"> -->

Referencia rápida quick-reference

Se proporciona información de referencia rápida como referencia.

Variables de contexto context-variables

Las variables de contexto:

{{properties}}                <!-- Main fragment metadata -->
{{fields}}                    <!-- Map keyed by field name to rendered values (such as strings, lists, nested maps for Content Fragment references, commerce maps, HTML, and others) -->
{{allFields}}                 <!-- List of { name, value } maps (uniform iteration) -->
{{hasFields}}.                <!-- Boolean -->
{{hasReferencedFragments}}.   <!-- Boolean -->
{{referencedFragments}}       <!-- List of referenced-fragment maps -->

Acceso al campo field-access

Cómo acceder a los campos:

{{{fields.fieldName}}}                    <!-- Direct field -->
{{{fields.author.name}}}                  <!-- Nested Content Fragment field -->
{{{fields.author.org.address.city}}}      <!-- Multi-level nesting -->
{{{fields.tags.[0]}}}                     <!-- Array by index -->
{{#each fields.tags}}...{{/each}}         <!-- Array iteration -->
{{{fields.authors.[0].name}}}             <!-- Multi-valued Content Fragment reference -->

Flujo de control control-flow

Flujo de control:

{{#if condition}}...{{/if}}               <!-- Conditional -->
{{#if condition}}...{{else}}...{{/if}}    <!-- If/else -->
{{#unless condition}}...{{/unless}}       <!-- Negative conditional -->
{{#each array}}...{{/each}}               <!-- Iteration -->
{{#each array}}...{{else}}...{{/each}}    <!-- Iteration with fallback -->
{{#with object}}...{{/with}}              <!-- Change scope -->

Variables de bucle loop-variables

Las variables de bucle:

{{@index}}        <!-- 0-based index -->
{{@number}}       <!-- 1-based index -->
{{@first}}        <!-- true for first item -->
{{@last}}         <!-- true for last item -->
{{@key}}          <!-- Object property name -->
{{this}}          <!-- Current item -->
{{../parent}}     <!-- Access parent scope -->

Ayudantes de plantilla personalizados custom-template-helpers

Los ayudantes de plantilla personalizados:

{{{asset fields.image class="css-class"}}}                <!-- Image with class -->
{{{asset fields.image class="c1" id="my-id"}}}            <!-- Image with multiple attrs -->
{{{asset fields.image alt="Custom alt text"}}}            <!-- Override alt text -->
{{{asset fields.image loading="lazy" data-x="val"}}}      <!-- Custom attributes -->

{{{text fields.title class="title-class"}}}               <!-- Span with class -->
{{{text fields.price class="price" id="p1"}}}             <!-- Span with multiple attrs -->
{{{text this class="item" data-index=@index}}}            <!-- In loops -->

Recursos adicionales additional-resources

Hay recursos adicionales disponibles:

recommendation-more-help
experience-manager-cloud-service-help-main-toc