Projeção de Conteúdo em Angular: O Guia para ng-content e ngTemplateOutlet

Bruno Donatelli - Feb 24 - - Dev Community

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 {}
Enter fullscreen mode Exit fullscreen mode

card.component.html

<article class="card">
  <header>
    <ng-content select="[title]"></ng-content>
  </header>
  <!-- body / content -->
  <section>
    <ng-content></ng-content>
  </section>
</article>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode
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>>();
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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

. . . . .