Appearance
Comment utiliser contentChild() sur Angular ?
Le contentChild() est une fonction qui permet d'accéder aux éléments enfants d'un composant. Imaginons une carte de profil qui contient un badge : la carte (composant parent) doit pouvoir accéder aux informations du badge (composant enfant).
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
Exemple concret
Imaginons que vous ayez une carte de visite où vous souhaitez accéder spécifiquement à un élément projeté, comme un titre ou une description. C'est comme si vous aviez une carte postale et que vous vouliez uniquement lire le message principal, sans regarder le reste.
Créons un exemple simple avec une carte de visite qui projette du contenu et accède à un élément spécifique :
ts
import { Component } from '@angular/core';
import { BusinessCardComponent } from './business-card.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [BusinessCardComponent],
template: `
<app-business-card>
<h2 #cardTitle>John Doe</h2>
<p>Développeur Web</p>
<p>Lorem ipsum dolor sit amet...</p>
</app-business-card>
`
})
export class AppComponent {}
ts
import { Component, contentChild, ElementRef } from '@angular/core';
@Component({
selector: 'app-business-card',
standalone: true,
template: `
<div>
<ng-content />
@if (title()) {
<div>
Titre de la carte : {{ title()?.nativeElement.textContent }}
</div>
}
</div>
`
})
export class BusinessCardComponent {
title = contentChild<ElementRef>('cardTitle');
}
Astuce
L'utilisation d'une référence template (#) est une façon simple et efficace de marquer un élément spécifique que vous souhaitez récupérer dans votre composant.
Dans cet exemple :
- Nous créons une référence
#cardTitle
sur l'élément h2 - Dans le composant BusinessCard, nous utilisons
contentChild()
pour accéder à cette référence - Le type
ElementRef
nous permet d'accéder aux propriétés de l'élément DOM
Attention
Assurez-vous que la référence template que vous recherchez existe bien dans le contenu projeté, sinon contentChild()
retournera undefined
.
Accéder à un composant enfant dans la projection de contenu
Créons un exemple avec deux composants : un badge et une carte de profil :
ts
import { Component } from '@angular/core';
import { ProfileCardComponent } from './profile-card.component';
import { BadgeComponent } from './badge.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [ProfileCardComponent, BadgeComponent],
template: `
<app-profile-card>
<app-badge></app-badge>
</app-profile-card>
`
})
export class AppComponent {}
ts
import { Component, computed, contentChild } from '@angular/core';
import { BadgeComponent } from './badge.component';
@Component({
selector: 'app-profile-card',
standalone: true,
template: `
<div class="card">
<h2>Profil Utilisateur</h2>
<ng-content />
@if (badgeStatus()) {
<p>Statut actuel : {{ badgeStatus() }}</p>
}
</div>
`,
styles: `
.card {
padding: 16px;
border: 1px solid #ddd;
border-radius: 8px;
}
`
})
export class ProfileCardComponent {
badge = contentChild(BadgeComponent);
badgeStatus = computed(() => this.badge()?.status);
}
ts
import { Component } from '@angular/core';
@Component({
selector: 'app-badge',
standalone: true,
template: `
<span class="badge">
{{ status }}
</span>
`,
styles: `
.badge {
padding: 4px 8px;
background-color: #4CAF50;
color: white;
border-radius: 4px;
}
`
})
export class BadgeComponent {
status = 'En ligne';
}
L'utilisation de contentChild()
retourne un signal, ce qui signifie que la valeur sera automatiquement mise à jour si l'élément enfant change.
Important
Le contentChild() retourne undefined si le composant enfant n'est pas trouvé. C'est pourquoi nous utilisons l'opérateur optionnel (?.) dans le computed.
Dans cet exemple :
- Le badge affiche un simple statut "En ligne"
- La carte de profil englobe le badge et affiche son statut
- La communication entre les composants se fait via contentChild()
Utiliser le décorateur @ContentChild
Avant Angular 17
Le décorateur @ContentChild
est la façon traditionnelle de définir des requêtes de vue dans Angular avant la version 17.
@ContentChild
est un décorateur Angular qui permet de récupérer une référence à un enfant de contenu d'un composant. Vous pouvez l'utiliser pour accéder à un élément HTML ou à une directive présente dans le contenu transcluded (inclus) d'un composant.
Voici un exemple de code montrant comment utiliser @ContentChild
:
ts
import { Component, ContentChild, ElementRef, AfterContentInit } from '@angular/core';
@Component({
selector: 'app-my-component',
standalone: true,
template: `
<ng-content></ng-content>
`
})
export class MyComponent implements AfterContentInit {
@ContentChild('myElement') myElement!: ElementRef;
ngAfterContentInit() {
console.log(this.myElement);
}
}
Dans cet exemple, nous avons déclaré une référence de contenu appelée myElement
qui référence l'élément HTML portant l'attribut #myElement
. Dans la méthode ngAfterContentInit
, nous pouvons accéder à l'élément en utilisant this.myElement
.
Pour utiliser ce composant, nous pouvons inclure du contenu dans le template de notre composant parent, comme ceci :
html
<app-my-component>
<div #myElement>Contenu inclus</div>
</app-my-component>
En utilisant @ContentChild
, vous pouvez accéder à tous les éléments et directives inclus dans le contenu transcluded de votre composant, ce qui peut être utile pour réaliser des opérations sur ces éléments ou directives.
Exemple
Supposons que vous avez un composant MyButton
qui affiche un bouton et qui prend en entrée un titre de bouton à afficher en utilisant une propriété de liaison de données. Maintenant, supposons que vous souhaitez utiliser ce composant MyButton
dans un autre composant, mais que vous souhaitiez ajouter une icône avant le titre du bouton. Vous pouvez utiliser @ContentChild
pour réaliser cette opération. Voici comment le code du composant parent pourrait ressembler :
ts
import { Component } from '@angular/core';
import { MyButtonComponent } from './my-button.component';
@Component({
selector: 'app-my-component',
standalone: true,
imports: [MyButtonComponent],
template: `
<app-my-button>
<i class="material-icons" #buttonIcon>favorite</i>
{{ buttonTitle }}
</app-my-button>
`
})
export class MyComponent {
buttonTitle = 'Mon bouton';
}
ts
import { Component, Input, ContentChild, ElementRef, AfterContentInit } from '@angular/core';
@Component({
selector: 'app-my-button',
standalone: true,
template: `
<ng-content></ng-content>
`
})
export class MyButtonComponent implements AfterContentInit {
@ContentChild('buttonIcon') buttonIcon!: ElementRef;
ngAfterContentInit() {
console.log(this.buttonIcon);
}
}
Dans ce code, nous avons un composant MyButton
qui affiche un bouton avec un titre et une icône. Le composant parent MyComponent
utilise MyButton
et inclut une icône et un titre dans le contenu "transcluded" du composant MyButton
. En utilisant @ContentChild
, nous pouvons accéder à l'icône et au titre inclus dans le contenu "transcluded" et réaliser des opérations sur eux, comme les afficher ou les modifier.
transcluded
Le terme "transcluded" fait référence au contenu inclus dans un composant parent et rendu dans le template du composant enfant. Cela permet d'inclure du contenu dynamique dans un composant sans avoir à le définir explicitement dans le template du composant enfant.