Aller au contenu principal

ngTemplateOutlet

Qu'est-ce que ngTemplateOutlet?

ngTemplateOutlet est une directive qui vous permet de rendre un template dynamiquement dans votre composant. Au lieu de dupliquer du HTML, vous pouvez définir un template une fois et le réutiliser plusieurs fois avec différentes données.

Pourquoi l'Utiliser?

  • Éviter la duplication de code - Définir le template une fois, l'utiliser plusieurs fois
  • Rendu dynamique - Basculer entre différents templates à l'exécution
  • Composants réutilisables - Permettre aux composants parents de personnaliser les templates enfants
  • Layouts conditionnels - Afficher différentes interfaces selon les conditions
  • Personnalisation de listes - Permettre un rendu personnalisé des éléments de liste

Quand l'Utiliser?

Cas d'UtilisationExemple
Patterns UI réutilisablesCartes, éléments de liste, modales
Layouts conditionnelsVues différentes pour utilisateurs connectés/déconnectés
Composants personnalisablesTableaux, listes avec templates de lignes personnalisés
Changement de templateLayouts différents selon la vue ou les préférences utilisateur
Éviter la duplicationMême structure HTML avec différentes données

Utilisation Basique

import { Component } from '@angular/core';
import { NgTemplateOutlet } from '@angular/common';

@Component({
selector: 'app-template-demo',
standalone: true,
imports: [NgTemplateOutlet],
template: `
<h1>Using ngTemplateOutlet</h1>

<!-- Render the template here -->
<ng-container *ngTemplateOutlet="greetingTemplate"></ng-container>

<!-- Define the template -->
<ng-template #greetingTemplate>
<p>Hello from template!</p>
<p>This can be reused anywhere!</p>
</ng-template>
`
})
export class TemplateDemoComponent {}

Passer des Données (Contexte)

@Component({
selector: 'app-album-template',
standalone: true,
imports: [NgTemplateOutlet],
template: `
<!-- Passer des données au template via le contexte -->
<ng-container
*ngTemplateOutlet="albumTemplate; context: {
$implicit: album,
index: 0,
isFeatured: true
}">
</ng-container>

<!-- Template receives data with 'let-' variables -->
<ng-template #albumTemplate let-album let-i="index" let-featured="isFeatured">
<div class="album-card">
<p>Album #{{ i }}: {{ album.name }}</p>
<p>Artist: {{ album.artist }}</p>
<span *ngIf="featured" class="badge">⭐ Featured</span>
</div>
</ng-template>
`
})
export class AlbumTemplateComponent {
album = { name: 'Dark Side of the Moon', artist: 'Pink Floyd' };
}

Reusable Templates

@Component({
selector: 'app-album-cards',
standalone: true,
imports: [NgTemplateOutlet],
template: `
<!-- Utiliser the same template 3 times with different data -->
<ng-container *ngTemplateOutlet="albumTemplate; context: {
name: 'Dark Side of the Moon',
artist: 'Pink Floyd'
}"></ng-container>

<ng-container *ngTemplateOutlet="albumTemplate; context: {
name: 'Abbey Road',
artist: 'The Beatles'
}"></ng-container>

<ng-container *ngTemplateOutlet="albumTemplate; context: {
name: 'Thriller',
artist: 'Michael Jackson'
}"></ng-container>

<!-- Define template once -->
<ng-template #albumTemplate let-name="name" let-artist="artist">
<div class="album-card">
<h2>{{ name }}</h2>
<p>🎤 {{ artist }}</p>
</div>
</ng-template>
`
})
export class AlbumCardsComponent {}

Result: Three album cards with the same structure but different content, without duplicating HTML.

Conditional Templates

@Component({
selector: 'app-player-view',
standalone: true,
imports: [NgTemplateOutlet],
template: `
<button (click)="isPlaying = !isPlaying">Toggle Playback</button>

<!-- Show different template based on condition -->
<ng-container *ngTemplateOutlet="
isPlaying ? playingTemplate : pausedTemplate
"></ng-container>

<ng-template #playingTemplate>
<div class="now-playing">
<h2>🎵 Now Playing: {{ trackTitle }}</h2>
<p>Your playlist is active</p>
<button (click)="pause()">⏸️ Pause</button>
</div>
</ng-template>

<ng-template #pausedTemplate>
<div class="paused">
<h2>⏸️ Playback Paused</h2>
<button (click)="play()">▶️ Resume</button>
</div>
</ng-template>
`
})
export class PlayerViewComponent {
isPlaying = false;
trackTitle = 'Bohemian Rhapsody';

play() { this.isPlaying = true; }
pause() { this.isPlaying = false; }
}

Customizable Components

import { Component, input, TemplateRef } from '@angular/core';
import { NgTemplateOutlet, NgFor } from '@angular/common';

@Component({
selector: 'app-list',
standalone: true,
imports: [NgTemplateOutlet, NgFor],
template: `
<div class="list-container">
<div *ngFor="let item of items(); let i = index" class="list-item">
<!-- Render cusaum template for each item -->
<ng-container *ngTemplateOutlet="itemTemplate(); context: {
$implicit: item,
index: i
}"></ng-container>
</div>
</div>
`
})
export class ListComponent {
// Basé sur signal inputs
items = input<any[]>([]);
itemTemplate = input.required<TemplateRef<any>>();
}

Real-World Use Case: Data Table

@Component({
selector: 'app-data-table',
standalone: true,
imports: [NgTemplateOutlet, NgFor],
template: `
<table>
<thead>
<tr>
<th *ngFor="let column of columns()">{{ column.label }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of data()">
<td *ngFor="let column of columns()">
<!-- Render cusaum cell template -->
<ng-container *ngTemplateOutlet="
column.cellTemplate;
context: { $implicit: row, column: column }
"></ng-container>
</td>
</tr>
</tbody>
</table>
`
})
export class DataTableComponent {
// Basé sur signal inputs
columns = input<Column[]>([]);
data = input<any[]>([]);
}

interface Column {
label: string;
cellTemplate: TemplateRef<any>;
}

Bonnes Pratiques

  • ✅ Utiliser pour la réutilisabilité des templates - Éviter la duplication de HTML
  • ✅ Excellent pour les composants personnalisables - Permettre aux consommateurs de définir l'apparence des éléments
  • ✅ Préférer aux chaînes *ngIf complexes - Plus propre que les conditionnels imbriqués
  • ✅ Utiliser $implicit pour la valeur de contexte principale - Syntaxe plus simple
  • ✅ Combiner avec contentChild() pour des patterns avancés - APIs plus flexibles
  • ❌ Ne pas surutiliser - Les composants simples n'ont pas besoin de templates
  • ❌ Éviter l'imbrication profonde - Garder les templates simples et lisibles

Common Patterns

PatternCas d'Utilisation
Un template, plusieurs rendusCartes, alertes, modales
Changement de templateLayouts différents selon l'état
Templates fournis par le parentListes, tableaux, grilles personnalisables
Templates conditionnelsVues connecté/déconnecté, UI admin/utilisateur
Template avec bouclesRendu de liste dynamique

ngTemplateOutlet vs Alternatives

ApprocheQuand l'Utiliser
ngTemplateOutletBesoin de réutiliser ou changer de templates dynamiquement
ngIf/ElseLogique simple d'affichage/masquage (2 options max)
ComponentComplex logic, lifecycle hooks needed
ngSwitchMultiple conditions based on single value