~2 min de lecture
Angular : Afficher dynamiquement des composants avec ngComponentOutlet
Dans cet article, on va explorer une astuce simple, mais puissante : générer dynamiquement des composants Angular
avec *ngComponentOutlet, notamment pour simplifier un menu répétitif.
Le problème : une interface répétitive
Imaginons un menu utilisateur comme celui-ci :
@Component({
selector: 'rc-toolbar-menu',
template: `
<!-- ... -->
<rc-menu-link label="Mon profil" routerLink="/profile">
<rc-profile-icon icon />
</rc-menu-link>
<rc-menu-link label="Les modules" routerLink="/modules">
<rc-modules-icon icon />
</rc-menu-link>
<!-- ... -->
`,
})
export class ToolbarMenu {
}
👉 C’est verbeux, difficile à maintenir, et impossible à itérer dynamiquement.
Objectif : un menu piloté par une simple structure de données
@Component(/* ... */)
export class ToolbarMenu {
protected readonly menuItems = [
{ label: 'Mon profil', routerLink: '/profile', icon: ProfileIcon },
{ label: 'Les modules', routerLink: '/modules', icon: ModulesIcon },
{ label: 'Bac à sable', routerLink: '/sandbox', icon: SandboxIcon },
{ label: 'Aide', routerLink: '/help', icon: HelpIcon },
];
}
Avec ça, on aimerait pouvoir faire :
@for (item of menuItems) {
<rc-menu-link [label]="item.label" [routerLink]="item.routerLink">
<!-- Insérer dynamiquement l'icône ici -->
</rc-menu-link>
}
Solution : *ngComponentOutlet
On va créer un composant utilitaire chargé d’injecter dynamiquement un autre composant via NgComponentOutlet.
Étape 1 : Le composant rc-menu-icon
import { ChangeDetectionStrategy, Component, input, Type } from '@angular/core';
import { NgComponentOutlet } from '@angular/common';
@Component({
selector: 'rc-menu-icon',
imports: [NgComponentOutlet],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `<ng-container *ngComponentOutlet="componentIcon()" />`,
})
export class MenuLinkIcon {
readonly componentIcon = input.required<Type<unknown>>();
}
Ce composant sert uniquement à projeter dynamiquement un composant Angular passé en entrée.
Étape 2 : Boucle dans le template
@Component({
selector: 'rc-toolbar-menu',
template: `
@for (item of menuItems; track item.label) {
<rc-menu-link [label]="item.label" [routerLink]="item.routerLink">
<rc-menu-icon [componentIcon]="item.icon" icon />
</rc-menu-link>
}
`,
})
export class ToolbarMenu {
protected readonly menuItems = [
{ label: 'Mon profil', routerLink: '/profile', icon: ProfileIcon },
{ label: 'Les modules', routerLink: '/modules', icon: ModulesIcon },
{ label: 'Bac à sable', routerLink: '/sandbox', icon: SandboxIcon },
{ label: 'Aide', routerLink: '/help', icon: HelpIcon },
];
}
🎯 Résultat : tu peux ajouter, retirer ou modifier un item sans toucher au template.
Avantages
- ✅ DRY : tu ne répètes plus chaque
rc-menu-link. - ✅ Extensible : une nouvelle entrée ? Un push dans
menuItemssuffit. - ✅ Lisibilité accrue : separation entre données et rendu.
- ✅ Performance : chaque composant est projeté dynamiquement en
OnPush.
À retenir
*ngComponentOutlet est sous-utilisé, mais c’est une pépite pour générer dynamiquement des UI composées, tout en
gardant un design system cohérent.
Tu peux l’utiliser pour un menu, une toolbar, une sidebar ou même des widgets personnalisables à la volée par l’utilisateur.
Si tu veux approfondir Angular moderne avec ce genre de patterns, jette un œil à EasyAngularKit 😉