Maitrisez ng-content Angular : projection de contenu, slots nommes avec select, ngProjectAs, contentChildren signal queries et patterns Tabs/Modal/Layout.
Pourquoi ng-content ? Composition vs configuration
Quand on apprend Angular, on commence par les @Input : on passe des chaînes, des objets, des callbacks au composant enfant. Très vite, le besoin évolue. Vous voulez créer une <app-card> qui accepte n'importe quel contenu HTML — un titre, un paragraphe, un graphique, plusieurs boutons. Faire passer ce contenu via une @Input revient à écrire du HTML dans une chaîne et à le rendre avec [innerHTML] : verbeux, dangereux, et incompatible avec les directives Angular.
C'est exactement le rôle de ng-content. La directive (héritée de la transclusion d'AngularJS, modernisée et standardisée dans Angular moderne) permet à un composant parent d'injecter du markup, des composants enfants ou des directives à des emplacements définis du template d'un composant hôte. Vous passez d'un mode configuration (je passe des valeurs) à un mode composition (je passe une structure entière).
Le pattern composition au cœur des design systems
Tous les composants UI sérieux (Material, PrimeNG, Taiga UI, ng-bootstrap) sont construits sur ce principe. Un MatCard n'expose aucune @Input pour son contenu : il fournit des slots <mat-card-header>, <mat-card-title>, <mat-card-content>, <mat-card-actions>, et c'est vous qui les remplissez. Cette inversion de contrôle (le consommateur fournit le markup, la lib fournit la structure) rend ces composants utilisables dans des contextes que leurs auteurs n'ont jamais anticipés.
Configuration vs composition — exemple
<!-- Configuration : limite à des valeurs -->
<app-card title="Statistiques" subtitle="Avril" [items]="rows"></app-card>
<!-- Composition : on passe une structure complète -->
<app-card>
<h3 cardTitle>Statistiques</h3>
<span cardSubtitle>Avril 2026</span>
<chart-line [data]="rows"></chart-line>
<button cardAction (click)="export()">Exporter</button>
</app-card>
Le second style produit du code radicalement plus flexible. La app-card ne sait rien du contenu — c'est le parent qui décide. Vous pouvez la réutiliser pour 50 contextes différents sans toucher à son implémentation. C'est le principe fondateur d'un design system Angular qui passe à l'échelle.
ng-content ouvre la porte au pattern composition, qui structure tous les composants UI sérieux de l'écosystème (Material, CDK, PrimeNG, Taiga UI). Maîtriser ses subtilités est la condition pour écrire des librairies Angular réutilisables.
Dans la suite de cet article, vous découvrirez comment fonctionne la projection sous le capot, les sélecteurs CSS pris en charge, la directive ngProjectAs, la lecture du contenu projeté via Signal Queries, les pièges classiques, et plusieurs cas pratiques directement réutilisables. Tous les exemples sont écrits en Angular moderne (Standalone Components, Signals, control flow @if/@for), exécutables tels quels dans une application Angular 17+.
Comment fonctionne la projection sous le capot
Quand Angular compile un template parent qui inclut <app-card>...contenu...</app-card>, il ne range pas ce contenu à l'intérieur du nœud app-card dans le DOM. À la place, il le conserve dans une structure appelée content view rattachée au composant parent. Au moment du rendu, ces nœuds sont déplacés physiquement à l'emplacement de chaque <ng-content> du template enfant.
Conséquences pratiques
- Le contenu projeté conserve son contexte parent — il continue d'accéder aux variables du parent, à ses templates locaux, à son injecteur.
- Le contexte de Change Detection est celui du parent, pas de l'enfant. C'est cohérent avec le point précédent.
- Le contenu est matché un seul fois — au moment de l'instanciation. Vous ne pouvez pas changer dynamiquement le select d'un
ng-contentaprès coup. - Les directives structurelles (
@if,@for,*ngIf) appliquées au contenu projeté sont évaluées dans le scope du parent — pas de l'enfant.
Schéma mental
// Côté parent — au compile-time, Angular extrait ces nœuds dans le content view
<app-card>
<span header>Titre</span> ← rangé dans content view, slot "[header]"
<p>Corps</p> ← rangé dans content view, slot par défaut
</app-card>
// Côté enfant — le moteur injecte les nœuds dans les slots correspondants
<div class="card">
<header><ng-content select="[header]"></ng-content></header> ← reçoit <span>
<main> <ng-content></ng-content></main> ← reçoit <p>
</div>
Cette compréhension est essentielle pour débuguer un cas tordu : « pourquoi mon composant projeté ne se met-il pas à jour quand l'état de l'enfant change ? » Réponse — parce que ses bindings sont évalués côté parent.
Projection simple — la Card minimaliste
Commençons par le cas trivial. Un seul ng-content, sans select : il capte tout le contenu passé par le parent.
// card.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-card',
standalone: true,
template: `
<article class="card">
<ng-content></ng-content>
</article>
`,
styles: [`
.card { border: 1px solid #e0e0e0; border-radius: 8px; padding: 16px; }
`],
})
export class CardComponent {}
Utilisation côté parent
<app-card>
<h3>Bienvenue</h3>
<p>Voici votre tableau de bord du jour.</p>
<button (click)="refresh()">Rafraîchir</button>
</app-card>
Le titre, le paragraphe et le bouton sont projetés dans l'unique ng-content. Le composant CardComponent fournit la coquille visuelle ; le parent fournit l'âme.
Important — pas d'ordre arbitraire
Quand vous utilisez un seul ng-content, les éléments du parent sont projetés dans leur ordre de déclaration. Vous ne pouvez pas réordonner depuis l'enfant — pour cela, il faut passer aux slots nommés de la section suivante.
Composer plutôt qu'étendre
Sans ng-content, le seul moyen de personnaliser une app-card serait de l'étendre via héritage ou de multiplier les @Input (title, subtitle, showIcon, iconType, actionsTemplate, etc.). L'API gonfle, les variations explosent, et chaque nouveau besoin ajoute une option. La projection inverse l'approche : le composant définit où on peut insérer du contenu, et le consommateur fournit librement le HTML adapté à son cas. Cette inversion suit le principe Open-Closed (ouvert à l'extension, fermé à la modification) — la fondation de toute API durable.
Multi-slot avec select — les sélecteurs supportés
Le mode multi-slot est le vrai super-pouvoir de ng-content. Vous déclarez plusieurs zones dans l'enfant, chacune avec un sélecteur CSS, et Angular dispatche le contenu projeté en fonction.
Composant Modal avec 3 slots
// modal.component.ts
@Component({
selector: 'app-modal',
standalone: true,
template: `
<div class="modal-overlay" (click)="close.emit()">
<div class="modal-box" (click)="$event.stopPropagation()">
<header class="modal-header">
<ng-content select="[modalTitle]"></ng-content>
</header>
<section class="modal-body">
<ng-content></ng-content> <!-- slot par défaut -->
</section>
<footer class="modal-actions">
<ng-content select="[modalAction]"></ng-content>
</footer>
</div>
</div>
`,
})
export class ModalComponent {
close = output<void>();
}
Utilisation
<app-modal (close)="dialog.set(false)">
<h2 modalTitle>Supprimer cet article ?</h2>
<p>Cette action est irréversible.</p>
<p>L'article et ses commentaires seront perdus.</p>
<button modalAction (click)="cancel()">Annuler</button>
<button modalAction (click)="confirm()" class="danger">Supprimer</button>
</app-modal>
Types de sélecteurs supportés
| Type | Syntaxe | Quand l'utiliser |
|---|---|---|
| Balise | select="header" | Quand le slot correspond à une balise sémantique |
| Attribut | select="[modalTitle]" | Recommandé — n'impose pas de balise au parent |
| Classe | select=".modal-title" | Pratique si la classe sert aussi au style |
| Composant | select="app-icon" | Pour matcher un composant Angular précis |
| Combiné | select="button[primary]" | CSS standard supporté |
[header], [footer]). Ils ne polluent pas le DOM avec des classes parasites, sont lisibles, et ne contraignent pas le parent à utiliser une balise précise.
ngProjectAs — projeter sous une autre identité
Cas typique : vous voulez utiliser ng-container pour grouper sans ajouter de balise, mais il ne porte aucun nom susceptible d'être matché par un select. Solution : la directive ngProjectAs ment au moteur en lui faisant croire que l'élément correspond à un autre sélecteur.
<app-card>
<!-- ng-container ne génère aucun nœud DOM,
mais ngProjectAs le fait reconnaître comme [cardHeader] -->
<ng-container ngProjectAs="[cardHeader]">
@if (user(); as u) {
<h3>{{ u.name }}</h3>
<small>{{ u.role }}</small>
}
</ng-container>
<p>Corps de la carte</p>
</app-card>
Quand l'utiliser
- Vous voulez insérer un bloc
@ifou@forqui doit être projeté dans un slot précis sans ajouter unedivparasite. - Vous projetez un composant tiers qui ne porte pas naturellement le bon sélecteur —
ngProjectAsagit comme une étiquette de routage. - Vous générez du contenu via
ng-templateetngTemplateOutletet devez l'aiguiller vers un slot précis. - Vous écrivez un composant de design system et voulez offrir une API stable même quand le consommateur utilise des constructions dynamiques (boucles, conditionnels) que vous ne maîtrisez pas.
Différence avec un sélecteur direct
On pourrait être tenté d'ajouter directement l'attribut cardHeader sur le ng-container : <ng-container cardHeader>. Cela ne fonctionne pas — ng-container ne génère aucun nœud DOM et n'est pas reconnu par les sélecteurs de projection standard. ngProjectAs est l'unique mécanisme qui informe explicitement le moteur de projection du nom à utiliser pour le matching, indépendamment de la nature de l'élément qui le porte.
Contenu par défaut et fallback intelligent
Depuis Angular 18, ng-content accepte du contenu par défaut entre ses balises ouvrante et fermante. Si le parent ne fournit rien pour ce slot, ce contenu prend la place. C'est le pattern recommandé pour des composants UI résilients.
@Component({
selector: 'app-panel',
template: `
<div class="panel">
<header class="panel-header">
<ng-content select="[panelIcon]">
<span class="icon-default">ℹ️</span>
</ng-content>
<ng-content select="[panelTitle]">
<span>Sans titre</span>
</ng-content>
</header>
<section class="panel-body">
<ng-content>
<p class="text-muted">Aucun contenu fourni.</p>
</ng-content>
</section>
</div>
`,
})
export class PanelComponent {}
Le parent peut maintenant utiliser <app-panel></app-panel> sans rien projeter, et obtenir une carte propre avec icône info, titre par défaut et message vide. C'est la garantie qu'aucun écran ne sera cassé visuellement par un oubli de slot.
ng-content exposé par un composant de design system devrait avoir un contenu par défaut. C'est la documentation visuelle implicite de l'API et un filet de sécurité pour vos consommateurs.
contentChild() et contentChildren() en Signal Queries
Le composant hôte a parfois besoin d'interagir avec le contenu projeté : connaître la liste des onglets, le composant actif, ou un input précis. C'est le rôle de contentChild() et contentChildren(), les versions signal d'@ContentChild/@ContentChildren introduites en Angular 17 et stabilisées en 18.
contentChildren() — accéder à plusieurs descendants projetés
// tab-group.component.ts
import { Component, contentChildren, signal, computed,
effect, AfterContentInit } from '@angular/core';
import { TabComponent } from './tab.component';
@Component({
selector: 'app-tab-group',
standalone: true,
template: `
<nav>
@for (tab of tabs(); track tab.id) {
<button
[class.active]="tab.id === active()"
(click)="active.set(tab.id)">
{{ tab.label() }}
</button>
}
</nav>
<ng-content></ng-content> <!-- les TabComponent réels sont projetés ici -->
`,
})
export class TabGroupComponent {
// Tous les TabComponent projetés sous app-tab-group, en Signal
tabs = contentChildren(TabComponent);
active = signal<string | null>(null);
constructor() {
// Initialise l'onglet actif au premier détecté
effect(() => {
if (!this.active() && this.tabs().length) {
this.active.set(this.tabs()[0].id);
}
});
}
}
Cas d'usage typique — un orchestrateur d'enfants
Le pattern orchestrateur d'enfants est l'un des plus puissants en Angular. Le composant hôte joue un rôle de coordination — il connaît la liste des enfants projetés via contentChildren(), gère un état partagé (onglet actif, panneau ouvert, ligne sélectionnée), et synchronise tous les enfants via des signaux. Côté consommateur, l'utilisation reste déclarative et naturelle : on liste les enfants entre les balises de l'hôte, sans avoir à les configurer un par un. C'est exactement ce que fait MatTabGroup avec ses MatTab, MatTable avec ses MatColumnDef, ou un composant Stepper avec ses Step.
Lifecycle — quand le contenu est-il disponible ?
ngAfterContentInitest appelé une fois, après que la projection initiale est terminée.ngAfterContentCheckedest appelé à chaque cycle après vérification du contenu projeté.- Avec les Signal Queries, vous n'avez plus besoin de vous reposer sur ces hooks — la query émet une valeur dès qu'elle est résolue, et toute lecture dans un
computed()ou uneffect()attend automatiquement la disponibilité.
contentChild() — accéder à un descendant unique
// form-field.component.ts
@Component({ /* ... */ })
export class FormFieldComponent {
// Le premier input HTML projeté à l'intérieur de app-form-field
private input = contentChild('input', { read: ElementRef });
ngAfterContentInit() {
// Ajoute automatiquement une classe pour le style
this.input()?.nativeElement.classList.add('form-control');
}
}
ng-content conditionnel et limites
ng-content n'est pas une directive structurelle. Vous ne pouvez pas écrire <ng-content *ngIf="x"> avec succès — la directive sera ignorée, et la projection se fera quand même. Pour rendre la projection conditionnelle, on enveloppe.
Pattern correct avec @if
@Component({
selector: 'app-alert',
template: `
<div class="alert">
@if (showIcon()) {
<span class="alert-icon">
<ng-content select="[alertIcon]">⚠️</ng-content>
</span>
}
<div class="alert-content">
<ng-content></ng-content>
</div>
</div>
`,
})
export class AlertComponent {
showIcon = input(true);
}
Pattern avancé — slot affiché selon la présence de contenu
Parfois on veut cacher un wrapper si le slot est vide. contentChildren() permet de détecter la présence.
@Component({
selector: 'app-card',
template: `
<div class="card">
@if (hasFooter()) {
<footer class="card-footer">
<ng-content select="[cardFooter]"></ng-content>
</footer>
}
<ng-content></ng-content>
</div>
`,
})
export class CardComponent {
// ⚠️ pseudo — détection robuste demande un wrapper directive ou ViewChild
private footerNodes = contentChildren('cardFooter', { descendants: true });
hasFooter = computed(() => this.footerNodes().length > 0);
}
En pratique, le contenu par défaut (section 6) couvre 90 % des besoins. La détection dynamique est utile dans les composants de design system les plus avancés.
Limites à connaître
Trois pièges à éviter quand vous mélangez ng-content et logique conditionnelle. Premièrement, ne tentez pas de déplacer dynamiquement un même ng-content entre deux emplacements selon une condition : Angular consommera la projection la première fois et la deuxième restera vide. Deuxièmement, plusieurs ng-content avec le même sélecteur ne dédoublent pas le contenu — le premier rencontré gagne, les suivants restent vides. Troisièmement, un ng-content dans une boucle @for projettera le même contenu à chaque itération, pas un contenu différent par index — pour cela, il faut un ng-template + ngTemplateOutletContext.
ng-content vs ng-template vs ng-container
Les trois éléments commencent par ng-, sont souvent confondus, et résolvent des problèmes différents. Voici la grille de décision.
| Élément | Rôle | Génère du DOM ? | Cas d'usage |
|---|---|---|---|
<ng-content> |
Recevoir du contenu projeté par le parent | Oui — le contenu projeté est rendu à cet emplacement | Cards, modales, layouts, composants de design system |
<ng-template> |
Déclarer un fragment de template non rendu, manipulable via TemplateRef |
Non — uniquement quand instancié par ngTemplateOutlet, @if, @for ou une directive structurelle |
Rendu conditionnel, slots dynamiques, listes virtuelles |
<ng-container> |
Grouper des éléments sans ajouter de wrapper DOM | Non — élément logique invisible | Appliquer une directive structurelle (@if, @for) à plusieurs nœuds sans <div> parasite |
Combinaison classique — projection de template dynamique
// Parent : prépare deux templates et un sélecteur
@Component({
template: `
<app-banner [tpl]="mode() === 'dark' ? dark : light"></app-banner>
<ng-template #light>
<p>Mode clair activé.</p>
</ng-template>
<ng-template #dark>
<p style="color:white">Mode sombre activé.</p>
</ng-template>
`,
})
export class HostComponent {
mode = signal<'light' | 'dark'>('light');
}
// Enfant : reçoit un TemplateRef et le rend via ngTemplateOutlet
@Component({
selector: 'app-banner',
imports: [NgTemplateOutlet],
template: `<ng-container *ngTemplateOutlet="tpl()"></ng-container>`,
})
export class BannerComponent {
tpl = input.required<TemplateRef<unknown>>();
}
Cette combinaison permet d'aller au-delà de ce que ng-content propose : passer plusieurs templates nommés et choisir lequel rendre à un emplacement précis. C'est le pattern interne de la plupart des composants Material (cellules de table, items d'autocomplete, etc.).
Patterns réels : Tabs, Accordion, FormField
Voici trois composants emblématiques où ng-content donne toute sa puissance, dans une version simplifiée mais directement utilisable.
1. Tabs — multi-slot + Signal queries
// tab.component.ts — un onglet, juste un wrapper
@Component({
selector: 'app-tab',
standalone: true,
template: `
@if (active()) {
<div class="tab-panel"><ng-content></ng-content></div>
}
`,
})
export class TabComponent {
id = input.required<string>();
label = input.required<string>();
active = signal(false);
}
// tab-group.component.ts — orchestrateur
@Component({
selector: 'app-tab-group',
standalone: true,
template: `
<nav class="tab-nav">
@for (t of tabs(); track t.id()) {
<button
[class.active]="t.id() === current()"
(click)="current.set(t.id())">
{{ t.label() }}
</button>
}
</nav>
<ng-content></ng-content>
`,
})
export class TabGroupComponent {
tabs = contentChildren(TabComponent);
current = signal<string | null>(null);
constructor() {
effect(() => {
const id = this.current();
for (const tab of this.tabs()) tab.active.set(tab.id() === id);
});
}
}
2. Accordion — autonomie de chaque panneau
@Component({
selector: 'app-accordion-panel',
template: `
<button class="ap-header" (click)="open.update(o => !o)">
<ng-content select="[panelTitle]">Sans titre</ng-content>
<span>{{ open() ? '−' : '+' }}</span>
</button>
@if (open()) {
<div class="ap-body">
<ng-content></ng-content>
</div>
}
`,
})
export class AccordionPanelComponent {
open = signal(false);
}
3. FormField — composition input + label + erreur
@Component({
selector: 'app-form-field',
template: `
<label>
<ng-content select="[fieldLabel]"></ng-content>
<span class="input-wrapper">
<ng-content></ng-content> <!-- l'input lui-même -->
</span>
</label>
@if (errors(); as e) {
<ul class="errors">
@for (msg of e; track msg) { <li>{{ msg }}</li> }
</ul>
}
`,
})
export class FormFieldComponent {
errors = input<string[] | null>(null);
}
Utilisation : <app-form-field [errors]="emailErrors()"><span fieldLabel>Email</span><input type="email" formControlName="email"></app-form-field>. Le composant ne sait rien du type d'input — il peut accueillir input, textarea, select ou n'importe quel composant tiers.
4. Layout — sidebar + main + footer
Pour structurer toute une page, on combine plusieurs slots avec un peu de CSS Grid. Le composant AppLayout ne sait rien du contenu — il fournit uniquement la structure responsive.
@Component({
selector: 'app-layout',
standalone: true,
template: `
<div class="layout">
<aside class="layout-sidebar">
<ng-content select="[appSidebar]"></ng-content>
</aside>
<main class="layout-main">
<ng-content></ng-content>
</main>
<footer class="layout-footer">
<ng-content select="[appFooter]"></ng-content>
</footer>
</div>
`,
styles: [`
.layout { display: grid; grid-template-columns: 240px 1fr;
grid-template-rows: 1fr auto; height: 100vh; }
.layout-sidebar { grid-row: 1 / span 2; background: #f5f5f5; }
.layout-footer { grid-column: 2; padding: 16px; border-top: 1px solid #ddd; }
@media (max-width: 768px) {
.layout { grid-template-columns: 1fr; }
.layout-sidebar { grid-row: auto; grid-column: 1; }
}
`],
})
export class AppLayoutComponent {}
Cette approche permet de réutiliser exactement la même structure de page sur tous les écrans de l'application, en injectant à chaque fois un sidebar, un main et un footer adaptés au contexte. Côté SEO, le HTML reste sémantique (aside, main, footer natifs), ce qui est important pour l'accessibilité et l'indexation.
Pièges, performance et bonnes pratiques
Pièges à connaître
- Le sélecteur doit matcher dès l'instanciation. Changer dynamiquement la classe ou l'attribut côté parent pour rediriger un élément vers un autre slot ne fonctionne pas — la projection est figée au compile-time.
- Le contenu projeté n'a pas accès au composant hôte par défaut. Pour partager de l'état, utilisez le pattern parent-token (DI) : l'hôte fournit un service via
providers: [], l'enfant projeté l'injecte. C'est ainsi que fonctionnent lesMatTabGroup/MatTab. - contentChildren() inclut par défaut uniquement les enfants directs. Pour matcher dans la profondeur, passez
{ descendants: true }en option. - Tester un composant à projection. Avec
TestBed, créez un host wrapper qui projette du contenu :@Component({ template: `<app-card>...</app-card>` })puis testez viaTestBed.createComponent(HostWrapper).
Performance
ng-content n'introduit aucun coût mesurable à l'exécution. La projection est résolue une fois, à l'instanciation. Le contenu projeté est déplacé dans le content view, pas copié — il existe en un seul exemplaire en mémoire, dans le contexte du parent. Le Change Detection traverse ce contexte une seule fois par tick, comme pour n'importe quel autre nœud du parent.
Bonnes pratiques de design
- Limitez le nombre de slots à 3–5 par composant. Au-delà, l'API devient illisible — découper en sous-composants est préférable.
- Préférez les sélecteurs par attribut sans valeur :
[cardHeader],[cardFooter]. Évitez les classes (qui se mélangent au CSS) et les balises (qui contraignent le parent). - Fournissez systématiquement un contenu par défaut dans chaque
ng-content, même s'il s'agit d'un simple espace réservé. - Documentez les slots disponibles en haut du fichier composant — c'est la seule documentation visible aux consommateurs.
- Préférez les Signal Queries (
contentChildren()) aux décorateurs@ContentChildrensur les nouveaux composants. - Quand vous avez besoin de plusieurs templates dynamiques sélectionnés à l'exécution, passez à
ng-template+ngTemplateOutletplutôt qu'à desng-contentconditionnels complexes.
Conclusion
ng-content n'est pas une fonctionnalité « avancée » d'Angular — c'est l'outil quotidien de tout développeur qui construit des composants réutilisables. Il transforme votre approche d'un mode configuration par Input à un mode composition par template, ouvre la voie aux design systems robustes, et reste, après dix ans, le mécanisme le plus stable du framework.
Couplé aux Signal Queries contentChild()/contentChildren(), à ngProjectAs, et aux nouveaux @if/@for d'Angular 17+, il permet de construire des composants UI qui rivalisent avec ceux de Material ou de la CDK avec quelques dizaines de lignes. La maîtriser, c'est entrer dans la catégorie des développeurs Angular capables d'écrire des librairies et non seulement de les consommer.
À l'échelle d'une équipe, l'adoption systématique de ng-content dans le design system interne change le travail quotidien : chaque nouvel écran consomme des briques composables plutôt que de réimplémenter des cards et des modales. Les revues de code deviennent plus simples, le CSS se réduit drastiquement, et l'accessibilité progresse car les wrappers sémantiques sont définis une fois pour toutes dans la lib. C'est l'investissement le plus rentable que vous puissiez faire sur un projet Angular durable.
- Préférer la composition par
ng-contentà la configuration par@Inputdès qu'on transmet du markup riche - Utiliser les slots nommés via
select="[attr]"pour les composants à plusieurs zones - Fournir un contenu par défaut entre les balises
<ng-content></ng-content> - Recourir à
ngProjectAspour aiguiller unng-containerou un template dynamique vers un slot précis - Lire le contenu projeté avec
contentChildren()/contentChild()en Signal Queries (Angular 17+) - Limiter à 3-5 slots par composant, au-delà découper en sous-composants
- Combiner
ng-template+ngTemplateOutletpour des slots dynamiques - Documenter les slots disponibles en commentaire du composant — c'est l'API publique
- Tester via un host wrapper qui projette le contenu attendu
- Se rappeler que le contenu projeté est rendu dans le contexte CD du parent, pas de l'enfant