Angular est-il vraiment plus compliqué que React ?

Quand on débute avec Angular, il est facile de se sentir découragé face à la multitude de concepts à assimiler. Cette complexité peut inciter à se tourner vers des frameworks comme React, qui semblent plus simples à première vue. Mais est-ce vraiment le cas ?

Abonnez-vous à notre chaîne

Pour profiter des prochaines vidéos sur Angular, abonnez-vous à la nouvelle chaîne YouTube !

Skip to content

Vous souhaitez recevoir de l'aide sur ce sujet ? rejoignez la communauté Angular.fr sur Discord.

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 :

  1. Nous créons une référence #cardTitle sur l'élément h2
  2. Dans le composant BusinessCard, nous utilisons contentChild() pour accéder à cette référence
  3. 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.

Chaque mois, recevez en avant-première notre newsletter avec les dernières actualités, tutoriels, astuces et ressources Angular directement par email !