Appearance
Comment utiliser contentChildren() sur Angular ?
Le contentChildren() est une fonction qui permet d'accéder à plusieurs éléments enfants d'un composant. Imaginez une liste de tâches où vous devez accéder à toutes les tâches individuelles pour les manipuler : la liste (composant parent) doit pouvoir interagir avec chaque tâche (composants enfants).
Utiliser les composants avec des signaux
Depuis Angular 17.x, les signaux sont introduits en tant que réactifs comme alternative aux décorateurs @Input()
, @Output()
, etc.
Donc, pour utiliser input()
, output()
, model()
, contentChild()
, contentChildren()
, viewChild()
et viewChildren()
, il est essentiel de comprendre la notion des signaux.
Lisez l'article sur les signaux pour en savoir plus.
Voir préalablement Maîtriser la projection de contenu avec ng-content dans Angular
Utiliser des sélecteurs spécifiques
Vous pouvez également utiliser contentChildren() pour sélectionner des éléments spécifiques en utilisant des références template :
ts
import { Component } from '@angular/core';
import { TaskListComponent } from './task-list.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [TaskListComponent],
template: `
<app-task-list>
<div #task class="high-priority">Tâche urgente</div>
<div #task>Tâche normale</div>
<div #task class="high-priority">Autre tâche urgente</div>
</app-task-list>
`
})
export class AppComponent {}
ts
import { Component, ElementRef, contentChildren, computed } from '@angular/core';
@Component({
selector: 'app-task-list',
standalone: true,
template: `
<div class="task-list">
<h2>Mes tâches</h2>
<ng-content />
<div class="priority-tasks">
<h3>Tâches prioritaires ({{ priorityTasksCount() }})</h3>
@for (task of priorityTasks(); track task) {
<div>{{ task.nativeElement.textContent }}</div>
}
</div>
</div>
`
})
export class TaskListComponent {
tasks = contentChildren<ElementRef>('task');
priorityTasks = computed(() =>
this.tasks().filter(task =>
task.nativeElement.classList.contains('high-priority')
)
);
priorityTasksCount = computed(() => this.priorityTasks().length);
}
Ce code met en place un système de gestion et de filtrage des tâches en utilisant les signaux d'Angular. Il commence par collecter toutes les tâches marquées avec la référence #task
, puis crée deux signaux dérivés : un pour filtrer les tâches prioritaires (celles avec la classe 'high-priority') et un autre pour compter ces tâches prioritaires.
L'intérêt principal est la réactivité : chaque fois que la liste des tâches change ou qu'une tâche devient prioritaire/non-prioritaire, les compteurs se mettent à jour automatiquement grâce aux signaux computed()
.
Récupérer componsants enfants projetés
Imaginons que vous ayez une liste d'utilisateurs où vous souhaitez accéder à tous les éléments projetés. C'est comme si vous aviez une boîte contenant plusieurs cartes de visite et que vous vouliez pouvoir les consulter toutes.
Créons un exemple simple avec une liste d'utilisateurs qui projette plusieurs cartes :
ts
import { Component } from '@angular/core';
import { UserListComponent } from './user-list.component';
import { UserCardComponent } from './user-card.component';
import { User } from './user';
@Component({
selector: 'app-root',
standalone: true,
imports: [UserListComponent, UserCardComponent],
template: `
<app-user-list>
@for (user of users; track user) {
<app-user-card [user]="user" />
}
</app-user-list>
`
})
export class AppComponent {
users: User[] = [
{ id: 1, name: 'John Doe', username: 'john', email: '[email protected]' },
{ id: 2, name: 'Jane Smith', username: 'jane', email: '[email protected]' },
{ id: 3, name: 'Bob Wilson', username: 'bob', email: '[email protected]' }
];
}
ts
import { Component, computed, contentChildren } from '@angular/core';
import { UserCardComponent } from './user-card.component';
@Component({
selector: 'app-user-list',
standalone: true,
template: `
<div class="user-list">
<h2>Liste des utilisateurs ({{ userCount() }})</h2>
<ng-content />
<div class="summary">
@if (activeUsers().length > 0) {
<p>Utilisateurs actifs : {{ activeUsers().length }}</p>
}
</div>
</div>
`
})
export class UserListComponent {
userCards = contentChildren(UserCardComponent);
userCount = computed(() => this.userCards().length);
activeUsers = computed(() =>
this.userCards().filter(card => card.isActive)
);
}
ts
import { Component, input } from '@angular/core';
@Component({
selector: 'app-user-card',
standalone: true,
template: `
<div class="card">
<h3>{{ user.name }}</h3>
<p>{{ user.email }}</p>
<button (click)="toggleActive()">
{{ isActive ? 'Actif' : 'Inactif' }}
</button>
</div>
`
})
export class UserCardComponent {
user = input.required<User>({} as User)
isActive = false;
toggleActive() {
this.isActive = !this.isActive;
}
}
ts
export interface User {
id: number;
name: string;
username?: string;
email: string;
address?: {
street: string;
suite: string;
city: string;
zipcode: string;
geo: {
lat: string;
lng: string;
}
};
phone?: string;
website?: string;
company?: {
name: string;
catchPhrase: string;
bs: string;
};
}
L'architecture se compose de trois composants qui travaillent ensemble :
AppComponent (Le conteneur principal)
- Détient la source de données (liste des utilisateurs)
- Projette dynamiquement des cartes utilisateur dans la liste
- Utilise
@for
pour créer une carte pour chaque utilisateur
UserListComponent (Le gestionnaire de projection)
- Agit comme un conteneur qui reçoit les cartes projetées
- Utilise
contentChildren()
pour accéder à toutes les cartes - Maintient des statistiques sur les cartes (total, actifs)
- Utilise
ng-content
pour afficher les cartes à l'endroit souhaité
UserCardComponent (L'élément projeté)
- Représente une carte individuelle
- Reçoit les données d'un utilisateur via input
- Gère son propre état (actif/inactif)
Astuce
contentChildren()
retourne un signal contenant un tableau des éléments enfants, ce qui permet de réagir automatiquement aux changements dans la liste des enfants.
Utiliser le décorateur @ContentChildren
Avant Angular 17
Le décorateur @ContentChildren
est la façon traditionnelle de définir des requêtes de vue multiples dans Angular avant la version 17.
Voici comment utiliser le décorateur @ContentChildren pour le même exemple :
ts
import { Component, ContentChildren, QueryList, AfterContentInit } from '@angular/core';
import { UserCardComponent } from './user-card.component';
@Component({
selector: 'app-user-list',
template: `
<div class="user-list">
<ng-content></ng-content>
</div>
`
})
export class UserListComponent implements AfterContentInit {
@ContentChildren(UserCardComponent) userCards!: QueryList<UserCardComponent>;
ngAfterContentInit() {
const array = this.userCards.toArray();
console.log(array);
}
}
QueryList
QueryList est une classe qui maintient une liste des éléments qui correspondent à une requête de contenu. Elle émet des événements lorsque la liste change.