Rich text com AEM Headless

O campo de texto multilinha é um tipo de dados dos Fragmentos de conteúdo que permite aos autores criar conteúdo rich text. As referências a outro conteúdo, como imagens ou outros Fragmentos de conteúdo, podem ser inseridas dinamicamente em linha no fluxo do texto. O campo Texto de linha única é outro tipo de dados dos Fragmentos de conteúdo que devem ser usados para elementos de texto simples.

A API GraphQL do AEM oferece uma capacidade robusta de retornar rich text como HTML, texto simples ou JSON puro. A representação JSON é poderosa, pois fornece ao aplicativo cliente controle total sobre como renderizar o conteúdo.

Editor de várias linhas

No Editor de fragmento de conteúdo, a barra de menus do campo de texto multilinha fornece aos autores recursos padrão de formatação de rich text, como negrito, itálico e sublinhado. A abertura do campo de várias linhas no modo de tela cheia habilita ferramentas de formatação adicionais, como o Tipo de parágrafo, localizar e substituir, verificação ortográfica e muito mais.

NOTE
Os plug-ins de rich text no editor de várias linhas não podem ser personalizados.

Tipo de dados de texto multilinha multi-line-data-type

Use o tipo de dados Texto de várias linhas ao definir seu Modelo de fragmento de conteúdo para habilitar a criação de rich text.

Tipo de dados de rich text de várias linhas

Várias propriedades do campo de várias linhas podem ser configuradas.

A propriedade Renderizar como pode ser definida como:

  • Área de texto - renderiza um único campo de várias linhas
  • Multiple Field - renderiza vários campos de várias linhas

O Tipo Padrão pode ser definido como:

  • Texto formatado
  • Markdown
  • Texto sem formatação

A opção Tipo Padrão influencia diretamente a experiência de edição e determina se as ferramentas de rich text estão presentes.

Você também pode habilitar referências embutidas a outros fragmentos de conteúdo marcando a Referência de fragmento de permissão e configurando os Modelos de fragmento de conteúdo permitidos.

Marque a caixa Traduzível se o conteúdo deve ser localizado. Somente Rich Text e Texto sem formatação podem ser localizados. Consulte trabalhando com conteúdo localizado para obter mais detalhes.

Resposta em rich text com a API do GraphQL

Ao criar uma consulta do GraphQL, os desenvolvedores podem escolher tipos de resposta diferentes de html, plaintext, markdown e json de um campo de várias linhas.

Os desenvolvedores podem usar a Visualização JSON no editor de fragmento de conteúdo para mostrar todos os valores do fragmento de conteúdo atual que podem ser retornados usando a API do GraphQL.

Consulta persistente do GraphQL

Selecionar o formato de resposta json para o campo de várias linhas oferece mais flexibilidade ao trabalhar com conteúdo rich text. O conteúdo rich text é entregue como uma matriz de tipos de nó JSON que podem ser processados exclusivamente com base na plataforma do cliente.

Abaixo está um tipo de resposta JSON de um campo de várias linhas chamado main que contém um parágrafo: "Este é um parágrafo que inclui conteúdo importante." onde "importante" está marcado como bold.

query ($path: String!) {
  articleByPath(_path: $path)
  {
    item {
      _path
      main {
        json
      }
    }
  }
}

A variável $path usada no filtro _path requer o caminho completo para o fragmento de conteúdo (por exemplo, /content/dam/wknd/en/magazine/sample-article).

Resposta do GraphQL:

{
  "data": {
    "articleByPath": {
      "item": {
        "_path": "/content/dam/wknd/en/magazine/sample-article",
        "main": {
          "json": [
            {
              "nodeType": "paragraph",
              "content": [
                {
                  "nodeType": "text",
                  "value": "This is a paragraph that includes "
                },
                {
                  "nodeType": "text",
                  "value": "important",
                  "format": {
                    "variants": [
                      "bold"
                    ]
                  }
                },
                {
                  "nodeType": "text",
                  "value": " content. "
                }
              ]
            }
          ]
        }
      }
    }
  }
}

Outros exemplos

Abaixo estão vários exemplos de tipos de resposta de um campo de várias linhas chamado main que contém um parágrafo: "Este é um parágrafo que inclui conteúdo importante." onde "importante" está marcado como bold.

HTML exemplo

Consulta persistente do GraphQL:

code language-graphql
query ($path: String!) {
  articleByPath(_path: $path)
  {
    item {
      _path
      main {
        html
      }
    }
  }
}

Resposta do GraphQL:

code language-json
{
  "data": {
    "articleByPath": {
      "item": {
        "_path": "/content/dam/wknd/en/magazine/sample-article",
        "main": {
            "html": "<p>This is a paragraph that includes <b>important</b> content.&nbsp;</p>\n"
        }
      }
    }
  }
}
Exemplo de Markdown

Consulta persistente do GraphQL:

code language-graphql
query ($path: String!) {
  articleByPath(_path: $path)
  {
    item {
      _path
      main {
        markdown
      }
    }
  }
}

Resposta do GraphQL:

code language-json
{
  "data": {
    "articleByPath": {
      "item": {
        "_path": "/content/dam/wknd/en/magazine/sample-article",
        "main": {
            "markdown": "This is a paragraph that includes **important** content. \n\n ",
        }
      }
    }
  }
}
Exemplo de texto sem formatação

Consulta persistente do GraphQL:

code language-graphql
query ($path: String!) {
  articleByPath(_path: $path)
  {
    item {
      _path
      main {
        plaintext
      }
    }
  }
}

Resposta do GraphQL:

code language-json
{
  "data": {
    "articleByPath": {
      "item": {
        "_path": "/content/dam/wknd/en/magazine/sample-article",
        "main": {
            "plaintext": "This is a paragraph that includes important content. ",
        }
      }
    }
  }
}

A opção de renderização plaintext elimina qualquer formatação.

Renderização de uma resposta JSON de rich text render-multiline-json-richtext

A resposta JSON de rich text do campo de várias linhas é estruturada como uma árvore hierárquica. Cada objeto ou nó representa um bloco HTML diferente do rich text.

Abaixo está uma amostra da resposta JSON de um campo de texto de várias linhas. Observe que cada objeto ou nó inclui um nodeType que representa o bloco HTML do rich text como paragraph, link e text. Cada nó opcionalmente contém content, que é uma submatriz que contém quaisquer filhos do nó atual.

"json": [// root "content" or child nodes
            {
                "nodeType": "paragraph", // node for a paragraph
                "content": [ // children of current node
                {
                    "nodeType": "text", // node for a text
                    "value": "This is the first paragraph. "
                },
                {
                    "nodeType": "link",
                    "data": {
                        "href": "http://www.adobe.com"
                    },
                    "value": "An external link"
                }
                ],
            },
            {
                "nodeType": "paragraph",
                "content": [
                {
                    "nodeType": "text",
                    "value": "This is the second paragraph."
                },
                ],
            },
]

A maneira mais fácil de renderizar a resposta de várias linhas do json é processar cada objeto ou nó na resposta e, em seguida, processar qualquer filho do nó atual. Uma função recursiva pode ser usada para atravessar a árvore JSON.

Abaixo está um exemplo de código que ilustra uma abordagem de travessia recursiva. As amostras são baseadas em JavaScript e usam o JSX do React, no entanto, os conceitos de programação podem ser aplicados a qualquer linguagem.

// renderNodeList - renders a list of nodes
function renderNodeList(childNodes) {

    if(!childNodes) {
        // null check
        return null;
    }

    return childNodes.map(node, index) => {
        return renderNode(node);
    }
}

renderNodeList é uma função recursiva que utiliza uma matriz de childNodes. Cada nó na matriz é passado para uma função renderNode, que, por sua vez, chama renderNodeList se o nó tiver filhos.

// renderNode - renders an individual node
function renderNode(node) {

    // if the current node has children, recursively process them
    const children = node.content ? renderNodeList(node.content) : null;

    // use a map to render the current node based on its nodeType
    return nodeMap[node.nodeType]?.(node, children);
}

A função renderNode espera um único objeto chamado node. Um nó pode ter filhos que são processados recursivamente usando a função renderNodeList descrita acima. Finalmente, um nodeMap é usado para renderizar o conteúdo do nó com base em seu nodeType.

// nodeMap - object literal that maps a JSX response based on a given key (nodeType)
const nodeMap = {
    'paragraph': (node, children) => <p>{children}</p>,
    'link': node => <a href={node.data.href} target={node.data.target}>{node.value}</a>,
    'text': node => node.value,
    'unordered-list': (node, children) => <ul>{children}</ul>,
    'ordered-list': (node, children) => <ol>{children}</ol>,
    'list-item': (node, children) => <li>{children}</li>,
    ...
}

nodeMap é um literal de Objeto JavaScript usado como mapa. Cada uma das "chaves" representa uma nodeType diferente. Parâmetros de node e children podem ser passados para as funções resultantes que renderizam o nó. O tipo de retorno usado neste exemplo é JSX. No entanto, a abordagem pode ser adaptada para criar um literal de string que representa o conteúdo HTML.

Exemplo de código completo

Um utilitário de renderização de rich text reutilizável pode ser encontrado no exemplo do WKND GraphQL React.

  • renderRichText.js - utilitário reutilizável que expõe uma função mapJsonRichText. Esse utilitário pode ser usado por componentes que desejam renderizar uma resposta JSON de rich text como JSX de reação.
  • AdventureDetail.js - Componente de exemplo que faz uma solicitação GraphQL que inclui rich text. O componente usa o utilitário mapJsonRichText para renderizar o rich text e quaisquer referências.

Adicionar referências embutidas ao rich text insert-fragment-references

O campo Múltiplo permite que os autores insiram imagens ou outros ativos digitais do AEM Assets no fluxo do rich text.

inserir imagem

A captura de tela acima representa uma imagem inserida no campo de várias linhas usando o botão Inserir ativo.

As referências a outros fragmentos de conteúdo também podem ser vinculadas ou inseridas no campo de várias linhas usando o botão Inserir fragmento de conteúdo.

Inserir referência de fragmento de conteúdo

A captura de tela acima representa outro fragmento de conteúdo, o Guia final para os parques de skate de Los Angeles, sendo inserido no campo de várias linhas. Os tipos de fragmentos de conteúdo que podem ser inseridos em um campo são controlados pela configuração Modelos de fragmento de conteúdo permitidos no tipo de dados de várias linhas no Modelo de fragmento de conteúdo.

Consultar referências em linha com o GraphQL

A API do GraphQL permite que os desenvolvedores criem um query que inclui propriedades adicionais sobre quaisquer referências inseridas em um campo de várias linhas. A resposta JSON inclui um objeto _references separado que lista essas propriedades extras. A resposta JSON fornece aos desenvolvedores controle total sobre como renderizar as referências ou links, em vez de precisar lidar com HTML opinativo.

Por exemplo, talvez você queira:

  • Inclua uma lógica de roteamento personalizada para gerenciar links para outros Fragmentos de conteúdo ao implementar um Aplicativo de página única, como usar o Roteador React ou o Next.js
  • Renderize uma imagem embutida usando o caminho absoluto para um ambiente AEM Publish como o valor src.
  • Determine como renderizar uma referência incorporada a outro fragmento de conteúdo com propriedades personalizadas adicionais.

Use o tipo de retorno json e inclua o objeto _references ao construir uma consulta GraphQL:

Consulta persistente do GraphQL:

query ($path: String!) {
  articleByPath(_path: $path, _assetTransform: { format: JPG, preferWebp: true })
  {
    item {
      _path
      main {
        json
      }
    }
    _references {
      ...on ImageRef {
        _dynamicUrl
        __typename
      }
      ...on ArticleModel {
        _path
        author
        __typename
      }
    }
  }
}

Na consulta acima, o campo main é retornado como JSON. O objeto _references inclui fragmentos para manipular quaisquer referências que sejam do tipo ImageRef ou do tipo ArticleModel.

Resposta JSON:

{
  "data": {
    "articleByPath": {
      "item": {
        "_path": "/content/dam/wknd/en/magazine/sample-article",
        "main": {
          "json": [
            {
              "nodeType": "paragraph",
              "content": [
                {
                  "nodeType": "text",
                  "value": "This is a paragraph that includes "
                },
                {
                  "nodeType": "text",
                  "value": "important",
                  "format": {
                    "variants": [
                      "bold"
                    ]
                  }
                },
                {
                  "nodeType": "text",
                  "value": " content. "
                }
              ]
            },
            {
              "nodeType": "paragraph",
              "content": [
                {
                  "nodeType": "reference",
                  "data": {
                    "path": "/content/dam/wknd/en/activities/climbing/sport-climbing.jpg",
                    "mimetype": "image/jpeg"
                  }
                }
              ]
            },
            {
              "nodeType": "paragraph",
              "content": [
                {
                  "nodeType": "text",
                  "value": "Reference another Content Fragment: "
                },
                {
                  "nodeType": "reference",
                  "data": {
                    "href": "/content/dam/wknd/en/magazine/la-skateparks/ultimate-guide-to-la-skateparks",
                    "type": "fragment"
                  },
                  "value": "Ultimate Guide to LA Skateparks"
                }
              ]
            }
          ]
        }
      },
      "_references": [
        {
          "_dynamicUrl": "/adobe/dynamicmedia/deliver/dm-aid--dd42d814-88ec-4c4d-b5ef-e3dc4bc0cb42/sport-climbing.jpg?preferwebp=true",
          "__typename": "ImageRef"
        },
        {
          "_path": "/content/dam/wknd/en/magazine/la-skateparks/ultimate-guide-to-la-skateparks",
          "author": "Stacey Roswells",
          "__typename": "ArticleModel"
        }
      ]
    }
  }
}

A resposta JSON inclui onde a referência foi inserida no rich text com o "nodeType": "reference". O objeto _references inclui cada referência.

Renderização de referências em linha em rich text

Para renderizar referências embutidas, a abordagem recursiva explicada em Renderizando uma resposta JSON de várias linhas pode ser expandida.

Onde nodeMap é o mapa que renderiza os nós JSON.

const nodeMap = {
        'reference': (node, children) => {

            // variable for reference in _references object
            let reference;

            // asset reference
            if (node.data.path) {
                // find reference based on path
                reference = references.find( ref => ref._path === node.data.path);
            }
            // Fragment Reference
            if (node.data.href) {
                // find in-line reference within _references array based on href and _path properties
                reference = references.find( ref => ref._path === node.data.href);
            }

            // if reference found, merge properties of reference and current node, then return render method of it using __typename property
            return reference ? renderReference[reference.__typename]({...reference, ...node}) : null;
        }
    }

A abordagem de alto nível é inspecionar sempre que um nodeType for igual a reference na resposta JSON de Várias Linhas. Uma função de renderização personalizada pode ser chamada, incluindo o objeto _references retornado na resposta do GraphQL.

O caminho de referência embutido pode ser comparado à entrada correspondente no objeto _references e outro mapa personalizado renderReference pode ser chamado.

const renderReference = {
    // node contains merged properties of the in-line reference and _references object
    'ImageRef': (node) => {
        // when __typename === ImageRef
        return <img src={node._dynamicUrl} alt={'in-line reference'} />
    },
    'ArticleModel': (node) => {
        // when __typename === ArticleModel
        return <Link to={`/article:${node._path}`}>{`${node.value}`}</Link>;
    }
    ...
}

O __typename do objeto _references pode ser usado para mapear diferentes tipos de referência para diferentes funções de renderização.

Exemplo de código completo

Um exemplo completo de como escrever um renderizador de referências personalizadas pode ser encontrado em AdventureDetail.js como parte do exemplo do WKND GraphQL React.

Exemplo completo

NOTE
O vídeo acima usa _publishUrl para renderizar a referência da imagem. Em vez disso, prefira o _dynamicUrl como explicado na apresentação de imagens otimizadas para a Web;

O vídeo anterior mostra um exemplo completo:

  1. Atualização de um campo de texto de várias linhas do modelo de fragmento de conteúdo para permitir Referências de fragmento
  2. Usar o Editor de fragmentos de conteúdo para incluir uma imagem e fazer referência a outro fragmento em um campo de texto de várias linhas.
  3. Criando uma consulta GraphQL que inclua a resposta de texto multilinha como JSON e qualquer _references usada.
  4. Escrever um SPA React que renderiza as referências em linha da resposta em rich text.
recommendation-more-help
e25b6834-e87f-4ff3-ba56-4cd16cdfdec4