Tudo o que você precisa saber sobre ng-template, ng-content, ng-container e *ngTemplateOutlet em Angular

Jul 1, 2021
admin

Foi um daqueles dias em que eu estava ocupado trabalhando em novas funcionalidades para o meu projeto de escritório. De repente, algo chamou minha atenção:

Final renderizado DOM em Angular

Apesar de inspecionar o DOM eu vi o ngcontent sendo aplicado em elementos pela Angular. Hmm… se eles contêm os elementos no DOM final, então qual é o uso de <ng-container>? Naquela época fiquei confuso entre <ng-container> e <ng-content>.

Na busca para saber as respostas às minhas perguntas descobri o conceito de <ng-template>. Para minha surpresa, havia também *ngTemplateOutlet. Comecei minha jornada buscando clareza sobre dois conceitos, mas agora eu tinha quatro deles, soando quase o mesmo!

Você já esteve nessa situação? Se sim, então você está no lugar certo. Então sem mais delongas vamos levá-los um a um.

><ng-template>

Como o nome sugere o <ng-template> é um elemento template que Angular usa com diretivas estruturais (*ngIf, *ngFor, e diretivas personalizadas).

Estes elementos template só funcionam na presença de diretivas estruturais. Angular envolve o elemento hospedeiro (ao qual a diretriz é aplicada) dentro de <ng-template> e consome o <ng-template> no DOM acabado, substituindo-o por comentários de diagnóstico.

Considerar um exemplo simples de *ngIf:

Exemplo 1- Processo angular de interpretação de diretrizes estruturais

Mostrada acima é a interpretação angular de *ngIf. Angular coloca o elemento hospedeiro ao qual a diretiva é aplicada dentro de <ng-template> e mantém o hospedeiro como ele é. O DOM final é semelhante ao que vimos no início deste artigo:

Exemplo 1- DOM final renderizado

Usagem:

Vimos como Angular usa <ng-template> mas e se quisermos usá-lo? Como estes elementos funcionam apenas com uma directiva estrutural, podemos escrever como:

Exemplo 2- Usando <ng-template>

Aqui home é uma boolean propriedade do componente definida para true valor. A saída do código acima em DOM:

Exemplo 2- Final renderizado DOM

Nada foi renderizada! 🙁

>

Mas porque não podemos ver a nossa mensagem mesmo depois de usar <ng-template> correctamente com uma directiva estrutural?

Este foi o resultado esperado. Como já discutimos, Angular substitui o <ng-template> por comentários de diagnóstico. Sem dúvida o código acima não geraria nenhum erro, pois Angular está perfeitamente bem com o seu caso de uso. Você nunca saberia o que exatamente aconteceu nos bastidores.

Vamos comparar os dois DOMs acima que foram renderizados pela Angular:

Exemplo 1 vs Exemplo 2

Se você observar de perto, há uma tag de comentário extra no DOM final do Exemplo 2. O código que Angular interpretou foi:

Processo de interpretação angular para o Exemplo 2

Angular embrulhou seu host <ng-template> dentro de outro <ng-template> e converteu não só o externo <ng-template> em comentários de diagnóstico, mas também o interno! É por isso que você não pôde ver nenhuma de suas mensagens.

Para se livrar disso, há duas maneiras de obter o resultado desejado:

O uso correto de <modelo de injeção>

Método 1:

Neste método, você está fornecendo o Angular com o formato dessugared, que não precisa de mais processamento. Desta vez a Angular apenas converteria <ng-template> para comentários mas deixaria o conteúdo dentro dela intocado (eles não estão mais dentro de nenhum <ng-template> como estavam no caso anterior). Assim, ele renderizará o conteúdo corretamente.

Para saber mais sobre como usar este formato com outras diretrizes estruturais, consulte este artigo.

Método 2:

Este é um formato bastante invisível e raramente é usado (usando dois irmãos <ng-template>). Aqui estamos dando uma referência ao modelo *ngIf em seu then para dizer-lhe qual modelo deve ser usado se a condição for verdadeira.

Usar múltiplos <ng-template> como este não é aconselhado (você poderia usar <ng-container> em seu lugar), pois não é para isto que eles são destinados. Eles são usados como um recipiente para templates que podem ser reutilizados em vários lugares. Cobriremos mais sobre isto numa secção posterior deste artigo.

>

<ng-container>

>

Deixe que alguma vez tenha escrito ou visto um código semelhante a este:

Exemplo 1

A razão pela qual muitos de nós escrevemos este código é a incapacidade de usar múltiplas directivas estruturais num único elemento hospedeiro em Angular. Agora este código funciona bem mas introduz vários vazios extra <div> no DOM se item.id é um valor falso que pode não ser necessário.

Exemplo 1- DOM final renderizado

Um pode não estar preocupado com um exemplo simples como este, mas para uma aplicação enorme que tem um DOM complexo (para exibir dezenas de milhares de dados) isto pode tornar-se problemático pois os elementos podem ter ouvintes ligados a eles que ainda estarão lá no DOM ouvindo eventos.

O que é ainda pior é o nível de aninhamento que você tem que fazer para aplicar o seu estilo (CSS)!

Image from: Inside Unbounce

Não se preocupe, nós temos <ng-container> para o resgate!

O Angular <ng-container> é um elemento de agrupamento que não interfere com estilos ou layout porque o Angular não o coloca no DOM.

Então se escrevermos o nosso Exemplo 1 com <ng-container>:

Exemplo 1 com <ng-container>

Recebemos o DOM final como:

>

DOM final renderizado com <ng-container>

Vemos que nos livramos daqueles vazios <div>s. Devemos usar <ng-container> quando só queremos aplicar múltiplas diretrizes estruturais sem introduzir nenhum elemento extra em nosso DOM.

Para mais informações, consulte os documentos. Há outro caso de uso onde é usado para injetar um template dinamicamente em uma página. Vou cobrir este caso de uso na última seção deste artigo.

>

<ng-content>

>

São usados para criar componentes configuráveis. Isto significa que os componentes podem ser configurados de acordo com as necessidades do seu utilizador. Isto é bem conhecido como Projecção de Conteúdos. Os componentes que são usados em bibliotecas publicadas fazem uso de <ng-content> para se tornarem configuráveis.

Configuração simples <project-content> componente:

Exemplo 1- <conteúdo do projeto> definição
Projeção de conteúdo com <conteúdo do projeto> componente

>

O conteúdo HTML passado dentro das tags de abertura e fechamento de <project-content> componente é o conteúdo a ser projetado. Isto é o que nós chamamos de Projecção de Conteúdo. O conteúdo será renderizado dentro do componente <ng-content>. Isto permite que o consumidor de <project-content> componente passe qualquer rodapé personalizado dentro do componente e controle exatamente como eles querem que ele seja renderizado.

Projeções múltiplas:

E se você pudesse decidir qual conteúdo deve ser colocado onde? Ao invés de cada conteúdo projetado dentro de um único <ng-content>, você também pode controlar como o conteúdo será projetado com o atributo select de <ng-content>. É necessário um seletor de elementos para decidir qual conteúdo será projetado dentro de um particular <ng-content>.

Aqui está como:

Exemplo 2- Projeção Multi-conteúdo com atualização <projeto-conteúdo>

Modificamos a definição de <project-content> para realizar a projeção Multi-conteúdo. O atributo select seleciona o tipo de conteúdo que será renderizado dentro de um particular <ng-content>. Aqui nós temos primeiro select para renderizar o cabeçalho h1 elemento. Se o conteúdo projetado não tiver um elemento h1, ele não renderizará nada. Da mesma forma o segundo select procura por um div. O resto do conteúdo é renderizado dentro do último <ng-content> sem nenhum select.

Chamar o componente parecerá:

Exemplo 2- Chamada de <conteúdo do projecto> componente no componente pai

*ngTemplateOutlet

…Eles são usados como um recipiente para templates que podem ser reutilizados em vários lugares. Cobriremos mais sobre isto numa secção posterior deste artigo.
…Há outro caso de uso em que é usado para injectar um template dinamicamente numa página. Vou cobrir este caso de uso na última seção deste artigo.

Esta é a secção onde discutiremos os dois pontos acima mencionados anteriormente. *ngTemplateOutlet é usado para dois cenários – para inserir um template comum em várias secções de uma vista independentemente de loops ou condição e para fazer um componente altamente configurado.

Reutilização de templates:

Considerar uma vista onde tem de inserir um template em vários locais. Por exemplo, um logotipo da empresa para ser colocado dentro de um website. Podemos consegui-lo escrevendo o template para o logotipo uma vez e reutilizando-o em qualquer lugar dentro da visualização.

Seguindo o código snippet:

Exemplo 1- Reutilização do template

Como você pode ver, nós apenas escrevemos o template do logotipo uma vez e o usamos três vezes na mesma página com uma única linha de código!

*ngTemplateOutlet também aceita um objeto de contexto que pode ser passado para personalizar a saída do template comum. Para mais informações sobre o objeto de contexto consulte os documentos oficiais.

Componentes customizáveis:

O segundo caso de uso para *ngTemplateOutlet é componentes altamente customizáveis. Considere nosso exemplo anterior de <project-content>componente com algumas modificações:

Exemplo 2- Fazendo componente customizável, project-content.html

Acima está a versão modificada de <project-content> componente que aceita três propriedades de entrada – headerTemplate, bodyTemplate, footerTemplate. A seguir está o snippet para project-content.ts:

Exemplo 2- Fazendo componente personalizável, project-content.ts

O que estamos tentando alcançar aqui é mostrar cabeçalho, corpo e rodapé como recebido do componente pai de <project-content>. Se algum deles não for fornecido, nosso componente irá mostrar o modelo padrão em seu lugar. Assim, criando um componente altamente personalizado.

Para usar nosso componente modificado recentemente:

Exemplo 2- Usando o componente modificado recentemente <conteúdo do projeto>

Esta é a forma como vamos passar o template refs para o nosso componente. Se algum deles não for passado então o componente irá renderizar o template padrão.

ng-content vs. *ngTemplateOutlet

Ambos nos ajudam a conseguir componentes altamente personalizados, mas quais escolher e quando?

Vemos claramente que *ngTemplateOutlet nos dá mais algum poder de mostrar o template padrão se nenhum for fornecido.

Este não é o caso com ng-content. Ele renderiza o conteúdo como está. No máximo você pode dividir o conteúdo e renderizá-los em diferentes locais de sua visualização com a ajuda do atributo select. Você não pode condicionalmente renderizar o conteúdo dentro de ng-content. Você tem que mostrar o conteúdo que é recebido do pai sem meios para tomar decisões baseadas no conteúdo.

No entanto, a escolha de selecionar entre os dois depende completamente do seu caso de uso. Pelo menos agora temos uma nova arma *ngTemplateOutlet em nosso arsenal que proporciona mais controle sobre o conteúdo, além das características de ng-content!

Deixe uma resposta

O seu endereço de email não será publicado.