Appearance
Maîtriser la projection de contenu avec ng-content dans Angular
La projection de contenu est un concept essentiel dans Angular qui permet de créer des composants vraiment réutilisables. Imaginez un cadre photo : le cadre reste le même, mais vous pouvez y mettre différentes photos. C'est exactement ce que fait ng-content
!
Comprendre ng-content avec un exemple concret
Prenons l'exemple d'une carte utilisateur réutilisable. Nous voulons créer un composant qui peut afficher différentes informations sur un utilisateur tout en gardant le même style.
CONSEIL
La projection de contenu est particulièrement utile pour créer des composants d'interface utilisateur réutilisables comme des cartes, des modales ou des panneaux.
ts
import { Component } from '@angular/core';
@Component({
selector: 'app-simple-card',
standalone: true,
template: `
<div class="simple-card">
<ng-content />
</div>
`,
styles: `
.simple-card {
padding: 16px;
border: 1px solid #ccc;
border-radius: 4px;
margin: 8px;
}
`
})
export class SimpleCardComponent {}
ts
import { Component } from '@angular/core';
import { SimpleCardComponent } from './simple-card.component';
@Component({
selector: 'app-home',
standalone: true,
imports: [SimpleCardComponent],
template: `
<app-simple-card>
<h2>{{ user.name }}</h2>
<p>{{ user.email }}</p>
</app-simple-card>
`
})
export class HomeComponent {
user: any = {
id: 1,
name: 'John Doe',
username: 'johndoe',
email: '[email protected]'
};
}
Dans cet exemple simple, tout le contenu placé entre les balises <app-simple-card>
sera projeté à l'endroit où se trouve le <ng-content>
dans le composant SimpleCardComponent
.
CONSEIL
Le contenu projeté peut être n'importe quel élément HTML, y compris d'autres composants. Et pas besoin d'importer <ng-content>
dans votre composant. Utilisez simplement <ng-content>
dans votre template.
Utilisation avancée avec les sélecteurs
Voici comment implémenter cette carte utilisateur :
ts
import { Component } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<div class="card">
<div class="card-header">
<ng-content select="[header]"></ng-content>
</div>
<div class="card-body">
<ng-content />
</div>
<div class="card-footer">
<ng-content select="[footer]"></ng-content>
</div>
</div>
`,
styles: `
.card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
}
`
})
export class UserCardComponent {}
ts
import { Component } from '@angular/core';
import { UserCardComponent } from './user-card.component';
@Component({
selector: 'app-home',
standalone: true,
imports: [UserCardComponent],
template: `
<app-user-card>
<div header>
<h2>{{ user.name }}</h2>
</div>
<div>
<p>Email: {{ user.email }}</p>
<p>Username: {{ user.username }}</p>
</div>
<div footer>
<button>Voir le profil</button>
</div>
</app-user-card>
`
})
export class HomeComponent {
user: any = {
id: 1,
name: 'John Doe',
username: 'johndoe',
email: '[email protected]'
};
}
Vous pouvez utiliser différents types de sélecteurs CSS pour cibler précisément le contenu à projeter :
- Par attribut :
select="[header]"
- Par classe :
select=".header"
- Par élément :
select="header"
Lorsque vous utilisez plusieurs ng-content
avec des sélecteurs, il est possible d'avoir un ng-content
par défaut qui capturera tout le contenu non sélectionné. L'ordre des ng-content
dans le template n'a pas d'importance.
Contenu de secours (Fallback Content)
Depuis Angular 18
La possibilité d'afficher un contenu de secours est disponible depuis Angular 18.
Une fonctionnalité très utile de ng-content
est la possibilité d'afficher un contenu de secours lorsqu'aucun contenu n'est projeté. C'est particulièrement utile pour fournir des valeurs par défaut.
ts
import { Component } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<div class="card-header">
<ng-content select="[header]">
<h2>Utilisateur Anonyme</h2>
</ng-content>
</div>
<div class="card-body">
<ng-content select="[body]">
<p>Aucune information disponible</p>
</ng-content>
</div>
`
})
export class UserCardComponent {}
ts
@Component({
selector: 'app-home',
standalone: true,
imports: [UserCardComponent],
template: `
<!-- Cette carte utilisera le contenu de secours -->
<app-user-card>
</app-user-card>
<!-- Cette carte affichera le contenu fourni -->
<app-user-card>
<div header>
<h2>{{ user.name }}</h2>
</div>
<div body>
<p>Email: {{ user.email }}</p>
</div>
</app-user-card>
`
})
ASTUCE
Le contenu de secours est particulièrement utile pour créer des composants qui restent fonctionnels même lorsque certaines parties du contenu ne sont pas fournies. Cela améliore la réutilisabilité et la robustesse de vos composants.
Alias de projection avec ngProjectAs
Angular fournit un attribut spécial ngProjectAs
qui permet de spécifier un sélecteur CSS sur n'importe quel élément. Lors de la projection de contenu, Angular utilisera la valeur de ngProjectAs
au lieu de l'identité de l'élément pour faire correspondre avec le sélecteur de ng-content
.
ts
import { Component } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<div class="card">
<ng-content select="[card-title]"></ng-content>
<div class="card-divider"></div>
<ng-content></ng-content>
</div>
`,
styles: `
.card {
border: 1px solid #ddd;
padding: 16px;
}
.card-divider {
border-top: 1px solid #eee;
margin: 8px 0;
}
`
})
export class UserCardComponent {}
ts
import { Component } from '@angular/core';
import { UserCardComponent } from './user-card.component';
@Component({
selector: 'app-home',
standalone: true,
imports: [UserCardComponent],
template: `
<app-user-card>
<h2 ngProjectAs="[card-title]">{{ user.name }}</h2>
<p>Email: {{ user.email }}</p>
</app-user-card>
`
})
export class HomeComponent {
user: User = {
id: 1,
name: 'John Doe',
email: '[email protected]'
};
}
Dans cet exemple, même si nous utilisons un élément <h2>
, l'attribut ngProjectAs="[card-title]"
permet de le projeter dans le ng-content
qui a le sélecteur select="[card-title]"
.
IMPORTANT
ngProjectAs
ne supporte que les valeurs statiques et ne peut pas être lié à des expressions dynamiques. Par exemple, ceci ne fonctionnera pas :
ts
<h2 [ngProjectAs]="dynamicSelector">Titre</h2>
UTILISATION
Cette fonctionnalité est particulièrement utile lorsque vous voulez projeter un élément HTML spécifique (comme un <h2>
ou un <p>
) dans un emplacement particulier, sans avoir à l'envelopper dans un conteneur supplémentaire.
Cas d'usage courants de ngProjectAs
Voici des situations courantes où l'utilisation de ngProjectAs
est particulièrement pertinente :
Scénario | Description | Avantage |
---|---|---|
Composants de navigation | Projection d'éléments <a> comme items de menu sans wrapper | Garde la sémantique HTML tout en permettant une projection ciblée |
Tableaux de données | Projection directe de cellules <td> dans différentes colonnes | Évite d'ajouter des divs supplémentaires qui casseraient la structure du tableau |
Formulaires | Projection de champs <input> dans différentes sections du formulaire | Maintient l'accessibilité native des éléments de formulaire |
Listes | Projection d'éléments <li> dans différentes sections de liste | Préserve la structure sémantique des listes |
En-têtes de section | Projection de titres (<h1> à <h6> ) dans des emplacements spécifiques | Conserve la hiérarchie des titres pour le SEO |
Composants de modal | Projection de boutons natifs dans différentes zones (header, footer) | Garde les avantages des éléments natifs pour l'accessibilité |
BONNE PRATIQUE
Utilisez ngProjectAs
quand vous voulez préserver la sémantique HTML et l'accessibilité tout en bénéficiant de la flexibilité de la projection de contenu.