Comprendre la différence entre ViewChild et ContentChild (composant parent-enfant)

Dans Angular, il existe deux syntaxes pour faire un lien parent-enfant entre deux composants : ViewChild et ContentChild. Comment utiliser chaque syntaxe et comment choisir la bonne ?

Syntaxe ViewChild

Si un composant A est affiché dans le template d’un composant B, alors A est un ViewChild de B.

// Ce composant sera l'enfant
@Component({
  selector: 'child',
  template: 'I am the child'
})
export class ChildComponent {}

// Ce composant est le parent : son template contient la balise de l'enfant
@Component({
  selector: 'parent',
  template: 'I am the parent: <child></child>'
})
export class ParentComponent {}

C’est généralement à cette syntaxe-là qu’on fait allusion lorsqu’on dit qu’un composant Angular est l’enfant d’un autre.

Avantages :

  • Encapsulation : Quand on affiche le parent, on ne doit pas se préoccuper de son contenu, qui peut être un assemblage complexe de plusieurs composants enfant.
  • Communication parent-enfant : Le parent peut très facilement transmettre des données à son enfant via des inputs, et recevoir des données de son enfant via des outputs. Voir #53 Comprendre les Inputs et Outputs de composant.

Inconvénient :

  • Le lien parent-enfant est totalement opaque. Quand on affiche le composant parent, impossible de savoir s’il a des enfants juste en regardant sa balise. Il faut aller voir sans son template pour vérifier ce qu’il contient.
<!-- Ce composant a-t-il des enfants ?! -->
<parent></parent>

Syntaxe ContentChild

On peut également créer un lien parent-enfant entre deux composants en écrivant la balise de l’enfant à l’intérieur de la balise du parent :

<parent>
  <child></child>
</parent>

On retrouve ici la même notion d’arborescence que dans une page HTML : une balise est enfant d’une autre si elle est affichée à l’intérieur d’une autre.

Dans Angular, on parle ici d’enfant ContentChild. Cette syntaxe est un peu moins fréquente que la syntaxe ViewChild.

Avantage :

  • Flexibilité : l’utilisateur peut changer le contenu affiché dans le parent à chaque utilisation, plutôt que de définir ce contenu une bonne fois pour toutes dans le template du parent. Cette technique est très utilisée pour créer une librairie de composants réutilisables, car on veut permettre aux utilisateurs de la librairie de personnaliser le contenu affiché dans les composants.

Inconvénient :

  • Impossible d’utiliser les inputs/outputs pour communiquer entre le parent et l’enfant.
  • Pas d’encapsulation. Puisque le parent et l’enfant sont affichés dans le même template, ils ont tous les deux accès au même scope, c’est-à-dire aux propriétés et méthodes publiques de la classe courante.

Pour rendre la syntaxe ContentChild possible, le composant parent doit utiliser la balise <ng-content> dans son template. Cette dernière sera automatiquement remplacée par le contenu qui se trouvait entre la balise ouvrante et la balise fermante du parent.

Ainsi, si on prend le composant parent suivant :

@Component({
  selector: 'parent',
  template: `
    <p>I am the parent.</p>
    <ng-content></ng-content>
  `
})
export class ParentComponent {}

Et qu’on l’utilise ainsi :

<parent>
  <em>Hello!</em>
</parent>

Cela produira le HTML suivant :

<p>I am the parent.</p>
<em>Hello!</em>  <!-- ng-content a été remplacé par le contenu du parent -->
Informations

Tags : components

Dernière mise à jour :

Auteur : AngularChef

Qualité : Bonne