Fragmentos de conteúdo visual - Modelos visual-content-fragments-templates

No Adobe Experience Manager (AEM) as a Cloud Service, os modelos do HTML podem ser usados para visualizar Fragmentos de conteúdo e entregá-los no formato HTML.

Os modelos do HTML permitem controlar como os fragmentos de conteúdo são exibidos. Você pode criar modelos do HTML em seu editor de código preferido e, em seguida, carregá-los e atribuí-los aos Modelos de fragmento de conteúdo no AEM. Os espaços reservados para conteúdo usando Handlebars.js permitem mapear o modelo para tipos de dados no Modelo de fragmento de conteúdo. Depois de atribuído a um modelo, um modelo fica disponível para ser usado com qualquer fragmento de conteúdo com base no modelo, para visualizar o fragmento ou entregá-lo como uma experiência modular no formato HTML para qualquer canal, por exemplo, web, email, aplicativo móvel ou outros.

Este artigo explica como criar modelos personalizados do HTML com sintaxe Handlebars para renderizar Fragmentos de conteúdo visual.

Depois de criar os modelos, você pode:

NOTE
Consulte Fragmentos de conteúdo visual para fazer upload, atribuir e usar seu modelo no AEM.
NOTE
Use o Trabalho de Fragmentos de Figma para Conteúdo Visual para automatizar o carregamento de um design do HTML.

O que você aprenderá what-you-will-learn

Depois de fornecer uma introdução (muito rápida) a:

  • Como usar seus modelos no AEM
  • Uso do URL de publicação

Esta página aborda (com mais detalhes):

  • Handlebars - as noções básicas necessárias da sintaxe
  • Como acessar os dados do fragmento de conteúdo
  • Trabalhar com fragmentos de conteúdo aninhados
  • Tratamento de campos de vários valores
  • Criação de loops e lógica condicional
  • Práticas recomendadas de design de modelo para fragmentos de conteúdo

Pré-requisitos prerequisites

Para entender e trabalhar com as tecnologias cobertas aqui, você deve ter:

  • Noções básicas sobre o HTML
  • Familiaridade com fragmentos de conteúdo e modelos de fragmento de conteúdo do AEM
  • Noções básicas sobre os modelos de fragmento de conteúdo

Uso de um modelo de HTML de fragmento de conteúdo using-a-content-fragment-html-template

Uso de um modelo de HTML de fragmento de conteúdo no AEM using-a-content-fragment-html-template-in-aem

Para obter detalhes sobre como usar o modelo no AEM, consulte:

Uso do URL de publicação do fragmento de conteúdo visual using-the-visual-content-fragment-publish-url

Depois de criar fragmentos de conteúdo visual usando o modelo, você pode usar a URL de publicação dos fragmentos de conteúdo visual.

Handlebars - os (muito) básicos handlebars-the-very-basics

Handlebars é uma linguagem de modelo simples que usa chaves duplas (colchetes) {{ }} para inserir conteúdo dinâmico no HTML.

Sintaxe básica basic-syntax

Um exemplo de sintaxe 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 }}

Principais conceitos key-concepts

Os principais conceitos do Handlebars:

Sintaxe
Descrição
Quando usar
{{ }}
Escapa caracteres especiais do HTML
Metadados, rótulos, booleanos
{{{ }}}
Gera HTML bruto (sem escape)
Rich text e saída de ativos
{{! }}
Comentário somente Handlebars
Documentação do modelo
IMPORTANT
Use chaves triplas ({{{ }}}) para valores de campo porque os valores são HTML pré-renderizados.

Referência de contexto do modelo template-context-reference

Quando o modelo é renderizado, ele recebe um objeto de contexto contendo todos os dados sobre o fragmento de conteúdo. Abrangerá:

  • o fragmento selecionado

  • todos os outros fragmentos referenciados do fragmento selecionado

    note
    NOTE
    Os fragmentos podem ser referenciados:
    • na interface do usuário do: até a profundidade máxima de 5
    • ao usar a API: a profundidade é configurável, até a profundidade máxima de 10

Fragmento de conteúdo content-fragment

A estrutura do objeto de contexto do Fragmento de conteúdo (selecionado):

Variável
Tipo
Descrição
properties
Mapa
Metadados do fragmento (consulte Estrutura de propriedades)
fields
Mapa
Acesso direto aos valores de campo por nome
allFields
Lista
Matriz de {name, value} para iteração
hasFields
Booleano
true se o fragmento tiver campos

Estrutura de propriedades properties-structure

O objeto properties tem a mesma estrutura para o fragmento selecionado e para cada fragmento referenciado.

Propriedade
Tipo
Descrição
Exemplo
id
String
UUID do fragmento
title
String
Título do fragmento
Ciclismo Sul de Utah
description
String
Descrição do fragmento
Uma aventura…
path
String
Caminho JCR para o fragmento
/content/dam/...
hasDescription
Booleano
Verdadeiro se a descrição não estiver em branco
true
createdDate
String
Data de criação ISO-8601
modifiedDate
String
Data de modificação ISO-8601
publishedDate
String
Data de publicação ISO-8601
status
String
Status de replicação da camada de Publicação
DRAFT
model
Mapa
Contém: id, path, name, technicalName, description
validationStatus
Lista
Entradas como {property, message}
previewReplicationStatus
String
Status de replicação da camada de Pré-visualização
tags
Lista
Tags de nível de fragmento. Cada item: id, title, titlePath, name, path, description
fieldTags
Lista
Tags de nível de campo. Mesma estrutura que tags.

Exemplos: Acesso ao modelo

Para o fragmento de conteúdo (selecionado):

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

Fragmentos de conteúdo referenciados referenced-content-fragments

A estrutura do objeto de contexto para qualquer fragmento referenciado:

Variável
Tipo
Descrição
hasReferencedFragments
Booleano
true quando existem referências
referencedFragments
Lista
Matriz de objetos de fragmento referenciados
referencesError
Booleano
true se ocorreu um erro ao carregar as referências
referencesErrorMessage
String
Mensagem de erro quando referencesError é true

Estrutura de fragmento referenciada referenced-fragment-structure

Cada item em referencedFragments contém:

Propriedade
Tipo
Descrição
anchorId
String
ID de âncora segura para HTML (no nível do fragmento; não uma propriedade de Fragmento de conteúdo)
properties
Mapa
Metadados do fragmento (mesma estrutura acima)
hasFields
Booleano
Verdadeiro se o fragmento tiver campos
fields
Mapa
Acesso direto a campos dentro deste fragmento
allFields
Lista
Matriz de {name, value} para iteração

Exemplos: Acesso ao modelo para o primeiro fragmento de conteúdo referenciado (primeiro item na lista de 0 indexados):

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

Ou no mapa de campos:

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

Acesso básico a campo basic-field-access

O acesso direto ao campo é recomendado; quando necessário, você pode iterar em todos os campos.

Acesse os campos diretamente pelo nome usando o 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>

Lembre-se:

  • Use chaves triplas {{{ }}} para valores de campo se eles contiverem HTML pré-renderizados (rich text)
  • Os nomes de campos (título, subtítulo, descrição, primaryImage) devem corresponder exatamente ao seu Modelo de Fragmento de Conteúdo 3}
  • Campos ausentes não são renderizados - nenhum erro é lançado e a sintaxe Handlebars permanece presente (e visível) no fragmento de HTML renderizado

Repetir em todos os campos iterate-through-all-fields

Use allFields quando não souber os nomes dos campos antecipadamente:

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

Lembre-se:

  • {{name}} usa chaves duplas (rótulo de texto simples)
  • {{{value}}} usa chaves triplas (valor HTML pré-renderizado)

Fragmentos de conteúdo aninhados nested-content-fragments

Quando um campo Fragmento de conteúdo faz referência a outro Fragmento de conteúdo, você pode usar a notação de pontos para acessar diretamente os campos no fragmento referenciado.

Aninhamento de nível único single-level-nesting

Um exemplo de aninhamento de nível único:

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

Padrão: fields.referenceFieldName.nestedFieldName

Aninhamento de vários níveis multi-level-nesting

O sistema oferece suporte a profundidade de aninhamento 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>

Padrão: fields.level1.level2.level3.fieldName (profundidade limitada; o padrão é 5, pode ser estendido para 10 ao usar a API)

Requisito do parâmetro de API: hidratação api-parameter-requirements

Para habilitar o acesso aninhado ao Fragmento de conteúdo, você deve incluir o parâmetro de consulta hydration na chamada de API:

Para ativar a hidratação:

# 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
O que está carregado
1
Fragmento principal + referências diretas
2
Fragmento principal + referências diretas + suas referências
3+
Continuar até 10 níveis

Campos de vários valores multi-valued-fields

Há vários tipos de campos de vários valores.

Campos de texto de vários valores multi-valued-text-fields

Texto, número, data e outros campos simples tornam-se matrizes quando com vários 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>

Lembre-se de que, ao acessar itens de matriz por índice em Handlebars:

  • Uso:
    • .[0] (ponto antes do colchete)
  • Não:
    • [0]

Campos de número de vários valores multi-valued-number-fields

Números são convertidos em cadeias de caracteres para renderização:

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

Referências de fragmentos de conteúdo de vários valores multi-valued-content-fragment-references

Quando um campo faz referência a vários Fragmentos de conteúdo:

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

Referências de ativos de vários valores multi-valued-asset-references

Os campos Referência de Conteúdo configurados para tipos de conteúdo que são ativos (por exemplo, imagens e documentos) são pré-renderizados como HTML. Ativos de vários valores se tornam arrays:

<!-- 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>

Referências aninhadas de vários valores nested-multi-valued-references

Referências de vários valores podem conter referências de vários valores em qualquer profundidade:

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

Loops e iteração loops-and-iteration

Handlebars fornece o auxiliar {{#each}} para iterar matrizes e objetos.

Iteração em arrays iterating-over-arrays

Um exemplo de iteração sobre matrizes:

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

Variáveis especiais em loops special-variables-in-loops

Dentro de {{#each}} blocos, Handlebars fornece variáveis especiais:

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

Iteração sobre fragmentos referenciados iterating-over-referenced-fragments

Um exemplo de iteração sobre 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}}

Loops aninhados nested-loops

Um exemplo de loops aninhados:

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

Renderização condicional conditional-rendering

Use as condições para mostrar ou ocultar o conteúdo com base na disponibilidade dos dados.

Se/Senão Básico basic-if-else

Um exemplo de uma construção 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 (condicional negativo) unless-negative-conditional

Um auxiliar unless:

<!-- Show author unless explicitly hidden -->
{{#unless fields.hideAuthor}}
<div class="author">{{{fields.author.name}}}</div>
{{/unless}}

Condicionais Aninhadas nested-conditials

Um exemplo de condicional aninhada:

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

Handlebars helpers embutidos built-in-handlebars-helpers

Handlebars inclui vários auxiliares internos, além de {{#if}} e {{#each}}.

Auxiliar
Descrição
{{#if condition}}
Renderiza o conteúdo se a condição for truthy. Valores falsy: false, undefined, null, 0, "", []
{{#unless condition}}
Renderiza o conteúdo se a condição for falsy (inverso de #if)
{{#each array}}
Repete o conteúdo para cada item; suporta {{else}} para matrizes vazias
{{#with object}}
Cria um novo escopo para um objeto aninhado, reduzindo a repetição de caminho
{{lookup this "key"}}
Pesquisa dinâmica uma propriedade por nome

Com Auxiliar with-helper

Cria um novo escopo para objetos aninhados para reduzir prefixos de caminho repetitivos:

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

Padrões avançados advanced-patterns

Seguem-se alguns exemplos de padrões avançados.

Acesso ao Contexto Pai em Loops Aninhados accessing-parent-context-in-nested-loops

Use ../ para acessar o escopo pai de dentro de um loop aninhado:

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

Classes CSS dinâmicas dynamic-css-classes

Um exemplo de classes 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>

Exemplos completos complete-examples

Vários exemplos completos são fornecidos para referência.

Publicação de blog com autor

Uma publicação do blog com detalhes do 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>

Chamada de API necessária:

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

Exibição de tabela genérica (sem conhecimento prévio de campos) generic-table-view-no-prior-knowledge-of-fields

Uma exibição de tabela genérica, sem um conhecimento inerente de campos. O é semelhante ao Modelo Genérico:

<!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áticas recomendadas best-practices

As práticas recomendadas incluem:

  1. Sempre use chaves triplas para valores de campo com conteúdo de marcação HTML.

    • Os valores de campo são HTML pré-renderizados.

      note
      NOTE
      Chaves duplas mostram tags HTML brutas como texto simples.
    code language-handlebars
    <!-- CORRECT -->
    {{{fields.description}}}
    
    <!-- WRONG - displays HTML tags as text -->
    {{fields.description}}
    
  2. Verifique a existência antes de acessar campos aninhados.

    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. Use o acesso direto ao campo quando possível.

    • É mais legível e manutenível do que iterar allFields e corresponder pelo nome.
  4. Modelos de estrutura com comentários de seção.

    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. Lidar com os dados ausentes normalmente com fallbacks.

    code language-handlebars
    {{#if fields.title}}
    <h1>{{{fields.title}}}</h1>
    {{snippet-not-found:else}}
    <h1>Untitled</h1>
    {{/if}}
    
  6. Sempre use uma estrutura de documento adequada do HTML.

    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. Teste com uma variedade de cenários de conteúdo:

    • Todos os campos totalmente preenchidos
    • Campos opcionais ausentes
    • Campos vazios com vários valores
    • Aninhamento profundo (vários níveis)
    • Referências que não são carregadas
  8. Usar elementos semânticos do HTML:

    • Para melhor acessibilidade, use <article>, <header>, <main>, <footer>, <time>, <address> ou similar.
  9. Mantenha os estilos em CSS.

    • Use marcas <style> ou folhas de estilos externas.
    • Evite estilos em linha sempre que possível.
  10. Lógica complexa de documento:

    • Use os comentários de Handlebars ({{! }}).
    • Não use comentários do HTML, que aparecem na saída renderizada.

Resolução de problemas troubleshooting

Algumas dicas de solução de problemas incluem:

Problema
Sintoma
Solução
O campo mostra as tags HTML como texto
<p>Hello World</p> exibido literalmente
Usar chaves triplas: {{{fields.description}}}
Os campos de Fragmento de conteúdo aninhados estão vazios ou mostram [objeto Objeto]
{{{fields.author.name}}} está em branco
Habilite a hidratação na chamada de API; verifique a ortografia do nome do campo; verifique se maxDepth é suficientemente profundo
O campo de vários valores mostra apenas o primeiro item
Matriz com cinco itens renderiza apenas um
Usar {{#each fields.tags}} para iterar todos os itens
O acesso ao índice de matriz não está funcionando
{{{fields.tags[0]}}} renderizações vazias
Usar sintaxe de colchetes: {{{fields.tags.[0]}}}
Fragmentos referenciados não aparecem
hasReferencedFragments é sempre falso
Habilitar hidratação: ?hydration=%7B%22enabled%22%3Atrue%7D; também verificar {{#if referencesError}}
O modelo não renderiza nada
Página vazia ou saída em branco
Verificar blocos {{#if}} ou {{#each}} não fechados; adicionar saída de diagnóstico: <pre>hasFields: {{hasFields}} | title: {{properties.title}}</pre>
Os comentários aparecem na página renderizada
Texto de comentário do HTML visível para usuários finais
Use os comentários {{! comment }} do Handlebars em vez do HTML <!-- comment -->
Condicional sempre é avaliado como verdadeiro
{{#if fields.enabled}} é sempre verdadeiro
Observação: a cadeia de caracteres "false" é verdadeira em Handlebars. Somente false, null, undefined, 0, "" e [] reais são falsy.
Caracteres especiais renderizados como entidades
&lt;, &amp; exibido em vez de <, &
Use chaves triplas para conteúdo HTML pré-renderizado: {{{fields.content}}}
Não é possível acessar a variável de loop externo a partir do loop interno
Variável do pai #each indefinida
Usar ../ para escopo pai: {{{../name}}}; usar ../../ para avô
A lista vazia não mostra a mensagem substituta
O campo de vários valores com zero itens não mostra nada
Usar {{else}} dentro de {{#each}}: {{#each fields.tags}}...{{else}}<p>No tags</p>{{/each}}

Trabalhar com ativos working-with-assets

O Assets referenciado dos fragmentos de conteúdo é pré-renderizado como HTML pelo AEM. Portanto, chaves triplas são obrigatórias para todas as referências de ativos.

Tipo de ativo
Renderizado como
Imagens
<img src="..." alt="...">
Vídeos
Elemento <video>
Documentos
Link <a href="...">

Lembre-se:

  • Sempre use chaves triplas para campos de ativos.
    O uso de chaves duplas escapará da tag HTML gerada e a exibirá como texto bruto, em vez de renderizar a imagem, o vídeo ou o link.

Uso do campo de ativo asset-field-usage

Um exemplo de uso de campo de ativo:

<!-- 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; -->

Auxiliares de modelo personalizado customer-template-helpers

O sistema fornece auxiliares do Handlebars personalizados para a geração de elementos de HTML com atributos personalizados de HTML. Esses auxiliares fornecem controle sobre a marcação gerada, enquanto lidam com a complexidade da extração de URLs de origem de conteúdo pré-renderizado.

Auxiliares disponíveis:

  1. asset - Gera <img> marcas com atributos personalizados
  2. text - Gera <span> tags quebrando o conteúdo do texto com atributos personalizados

asset auxiliar asset-helper

Sintaxe:

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

Lembre-se:

  • Use chaves triplas {{{ }}} com o auxiliar de ativos, não chaves duplas!

Quatro exemplos básicos four-basic-examples

Quatro exemplos básicos são:

<!-- 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 suportados supported-attributes

Você pode adicionar qualquer atributo válido do HTML:

Atributo
Exemplo
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;"

Substituir texto alternativo override-alt-text

O atributo alt da imagem original pode ser substituído:

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

Exemplo complexo complex-example

Um exemplo complexo é:

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

Uso com loops using-with-loops

Auxiliar de ativo em loops:

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

text auxiliar text-helper

O auxiliar de texto gera um conteúdo de texto de quebra automática de tag <span> com classes CSS personalizadas e atributos HTML. Útil para estilizar campos de texto individuais.

Sintaxe:

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

Lembre-se:

  • Use chaves triplas {{{ }}} com o auxiliar de texto, não chaves duplas!

Três exemplos básicos three-basic-examples

Três exemplos básicos são:

<!-- 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 comuns common-use-cases

Alguns casos de uso comuns incluem:

<!-- 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"}}}

Com loops with-loops

Um caso de uso comum com loops inclui:

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

Auxiliares - Validação de atributo helpers-attribute-validation

Ambos os auxiliares validam nomes de atributos antes de incluí-los na saída.

Nomes de atributo válidos:

  • Deve começar com uma letra (a-z, A-Z)

  • Pode conter apenas letras, dígitos, hifens e sublinhados; consulte as Convenções de nomenclatura

  • Não diferencia maiúsculas de minúsculas

  • Por exemplo:

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

Nomes de atributos inválidos são ignorados silenciosamente com um aviso nos logs:

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

Lembre-se:

  • Verifique nos logs do servidor se há avisos de “Formato de nome de atributo inválido bloqueado”.

Comparação de saída direta com auxiliares comparing-direct-output-to-helpers

Quando gerar saída direta {{{fields.xxx}}}:

  • Você não precisa de um estilo personalizado
  • Você deseja a saída padrão como está
  • O campo contém HTML complexos que você não deseja modificar

Quando usar auxiliares:

  • É necessário adicionar classes CSS para o estilo
  • Você precisa adicionar atributos personalizados do HTML (data-*, aria-* e outros)
  • Você deseja uma estrutura consistente e controlada do HTML

Comparação:

<!-- 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"> -->

Referência rápida quick-reference

Algumas informações de referência rápida são fornecidas para referência.

Variáveis de contexto context-variables

As variáveis 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 -->

Acesso ao campo field-access

Como acessar 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 -->

Fluxo de controle control-flow

O fluxo de controle:

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

Variáveis de loop loop-variables

As variáveis de loop:

{{@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 -->

Auxiliares de modelo personalizado custom-template-helpers

Os assistentes do modelo personalizado:

{{{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 adicionais additional-resources

Recursos adicionais estão disponíveis:

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