Você já tentou adicionar um trecho de código HTML dentro de um componente Angular, mas o conteúdo não foi projetado como esperado? Isso pode parecer um desafio dentro do framework, principalmente quando se deseja criar componentes reutilizáveis. Felizmente, Angular oferece ferramentas poderosas para essa tarefa: ng-content
e ngTemplateOutlet
.
Antes de começar a leitura abaixo, eu gostaria que você entendesse os conceitos de ng-content, ng-template, ng-container e *ngTemplateOutlet nesse artigo da freeCodeCamp: Tudo o que você precisa saber sobre ng-template, ng-content, ng-container e *ngTemplateOutlet em Angular
O que é ng-content?
A diretiva ng-content permite a projeção de conteúdo dentro do componente, trabalhando de forma similar ao elemento HTML nativo slot
, porém com algumas funcionalidades Angular.
Em suma, Essa diretiva funciona como um espaço reservado dentro de um componente, permitindo a inserção de conteúdo externo. Isso é particularmente útil quando se desenvolvem componentes que precisam ser altamente reutilizáveis e personalizados.
Exemplo básico de uso do ng-content
Vamos considerar um exemplo simples de um componente card
que pode exibir conteúdo dinâmico:
card.component.ts
import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'app-card',
templateUrl: './card.component.html',
styleUrl: './card.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CardComponent {}
card.component.html
<article class="card">
<header>
<ng-content select="[title]"></ng-content>
</header>
<!-- body / content -->
<section>
<ng-content></ng-content>
</section>
</article>
Neste exemplo, utilizamos a diretiva ng-content de duas maneiras:
- Com o atributo select, para especificar qual parte do conteúdo projetado deve aparecer em diferentes seções do componente
- Sem o atributo select, para projeção geral do componente.
A utilização do card fica desta forma:
app.parent.html
<app-card>
<!-- ng-content select title attribute -->
<h2 title>Lorem Ipsum</h2>
<!-- ng-content general -->
<div>
<p>
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Voluptatum fuga
reprehenderit adipisci expedita repellendus dolorem non laborum fugiat,
voluptatem odit nihil quae rem laudantium. Quos deserunt numquam quae
totam doloribus.
</p>
</div>
</app-card>
Com o componente card
estruturado dessa forma, temos a flexibilidade de projetar conteúdos textuais, ou conteúdos com imagens - as possibilidades são infinitas!
Mas calma aí... isso ainda é muito estático, e eu procuro uma abordagem mais dinâmica. Tem jeito? TEM! Vamos para a próxima parte do artigo.
O que é ngTemplateOutlet?
Enquanto o ng-content é ótimo para a projeção de conteúdo estático, a diretiva ngTemplateOutlet oferece uma abordagem mais dinâmica, permitindo a projeção de conteúdo em tempo de execução.
Exemplo
- Vamos adicionar uma mudança no nosso componente card, com um input de TemplateRef para podermos passar a projeção de conteúdo que queremos.
- No template HTML, vamos remover todos os ng-content, e passar um ng-container que contém a diretiva de *ngTemplateOutlet, que vai receber o TemplateRef
<article class="card">
<ng-container *ngTemplateOutlet="cardTemplateRef() ?? null"></ng-container>
</article>
import { CommonModule } from '@angular/common';
import { ChangeDetectionStrategy, Component, input, TemplateRef } from '@angular/core';
@Component({
selector: 'app-card',
standalone: true,
imports: [CommonModule],
templateUrl: './card.component.html',
styleUrl: './card.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CardOutletComponent {
cardTemplateRef = input<TemplateRef<unknown>>();
}
Lembrando que precisamos adicionar o CommonModule para termos acesso ao *ngTemplateOutlet
E a utilização do nosso card
com TemplateRef:
<app-card [cardTemplateRef]="textTemplate"></app-card-outlet>
<app-card [cardTemplateRef]="imageTemplate"></app-card-outlet>
<ng-template #textTemplate>
<header>
<h2>Lorem Ipsum</h2>
</header>
<section>
<div>
<p>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Neque, fuga
officiis ex ducimus, cupiditate, id culpa ipsa eligendi maiores autem
quod architecto eum veniam expedita omnis. Vitae debitis quibusdam a?
</p>
</div>
</section>
</ng-template>
<ng-template #imageTemplate>
<header>
<h2>Lorem Ipsum</h2>
</header>
<section>
<img
src="path/to/image"
alt="my alt text IS IMPORTANT MAKE SURE YOU ARE ENHANCING A11Y IN YOUR PROJECT"
/>
<p>
Lorem ipsum, dolor sit amet consectetur adipisicing elit. Quia, tenetur
recusandae? Quo minus iusto nihil explicabo assumenda recusandae magnam
quia ipsum sint impedit, totam, nobis amet libero, dolorem harum cumque.
</p>
</section>
</ng-template>
Comparação
Embora ng-content e ngTemplateOutlet sejam usados para projeção de conteúdo, eles têm diferentes casos de uso e benefícios:
- ng-content: ideal para conteúdo estático. Permite definir pontos específicos no componente para a inserção de conteúdo externo, sendo excelente para componentes de layout (como o nosso card).
- ngTemplateOutlet: Melhor para projeção de conteúdo dinâmico. Permite a inserção de templates diferentes com base em condições lógicas ou variável de referência no ng-template. Oferece maior flexibilidade e controle em tempo de execução.
Conclusão
Entender e usar ng-content e ngTemplateOutlet pode aumentar significativamente a reutilização e a flexibilidade dos seus componentes Angular.
Ao entender e dominar essas funcionalidades, você estará mais bem equipado para criar aplicações Angular altamente dinâmicas e personalizáveis.
Referências
- https://angular.dev/guide/components/content-projection
- https://www.freecodecamp.org/news/everything-you-need-to-know-about-ng-template-ng-content-ng-container-and-ngtemplateoutlet-4b7b51223691/
- https://medium.com/senior/criando-componente-angular-com-conteudo-dinamico-ng-content-82334babe134
- https://stackoverflow.com/questions/52638718/multiple-ng-content
- https://andrewrosario.medium.com/ngtemplateoutlet-o-segredo-da-personaliza%C3%A7%C3%A3o-6f3203787a56
- https://blog.angular-university.io/angular-ng-template-ng-container-ngtemplateoutlet/
- https://consolelog.com.br/customizando-componente-com-ngtemplateoutlet-angular-2/