Tout ce que vous devez savoir sur ng-template, ng-content, ng-container et *ngTemplateOutlet en Angular
C’était un de ces jours où j’étais occupé à travailler sur de nouvelles fonctionnalités pour mon projet de bureau. Tout à coup, quelque chose a attiré mon attention :
En inspectant le DOM, j’ai vu le ngcontent
appliqué sur les éléments par Angular. Hmm… s’ils contiennent les éléments dans le DOM final, alors quelle est l’utilité des <ng-container>
? À ce moment-là, j’ai été confondu entre <ng-container>
et <ng-content>
.
Dans la quête de connaître les réponses à mes questions, j’ai découvert le concept de <ng-template>
. À ma grande surprise, il y avait aussi *ngTemplateOutlet
. J’ai commencé mon voyage en cherchant la clarté sur deux concepts, mais maintenant j’en avais quatre, sonnant presque pareil !
Vous avez déjà été dans cette situation ? Si oui, alors vous êtes au bon endroit. Alors sans plus attendre, prenons-les un par un.
<ng-template>
Comme son nom l’indique, le <ng-template>
est un élément de template qu’Angular utilise avec les directives structurelles (*ngIf
, *ngFor
, et les directives personnalisées).
Ces éléments de template ne fonctionnent qu’en présence de directives structurelles. Angular enveloppe l’élément hôte (auquel la directive est appliquée) à l’intérieur de <ng-template>
et consomme le <ng-template>
dans le DOM fini en le remplaçant par des commentaires de diagnostic.
Considérez un exemple simple de *ngIf
:
La figure ci-dessus est l’interprétation Angular de *ngIf
. Angular place l’élément hôte auquel la directive est appliquée dans <ng-template>
et garde l’hôte tel quel. Le DOM final est similaire à ce que nous avons vu au début de cet article:
Utilisation:
Nous avons vu comment Angular utilise <ng-template>
mais que faire si nous voulons l’utiliser ? Comme ces éléments ne fonctionnent qu’avec une directive structurelle, nous pouvons écrire comme:
Ici home
est une propriété boolean
du composant définie à la valeur true
. La sortie du code ci-dessus dans le DOM:
Rien n’a été rendu ! 🙁
Mais pourquoi ne pouvons-nous pas voir notre message même après avoir utilisé <ng-template>
correctement avec une directive structurelle ?
C’était le résultat attendu. Comme nous l’avons déjà discuté, Angular remplace les <ng-template>
par des commentaires de diagnostic. Il ne fait aucun doute que le code ci-dessus ne générerait aucune erreur, car Angular s’adapte parfaitement à votre cas d’utilisation. Vous ne sauriez jamais ce qui s’est exactement passé dans les coulisses.
Comparons les deux DOM ci-dessus qui ont été rendus par Angular:
Si vous regardez attentivement, il y a une balise de commentaire supplémentaire dans le DOM final de l’exemple 2. Le code qu’Angular a interprété était :
Angular a enveloppé votre <ng-template>
hôte dans un autre <ng-template>
et a converti non seulement le <ng-template>
extérieur en commentaires de diagnostic, mais aussi l’intérieur ! C’est pourquoi vous n’avez pu voir aucun de vos messages.
Pour se débarrasser de cela, il y a deux façons d’obtenir le résultat souhaité :
Méthode 1:
Dans cette méthode, vous fournissez à Angular le format désucré qui n’a pas besoin de traitement supplémentaire. Cette fois, Angular ne ferait que convertir les <ng-template>
en commentaires mais laisse le contenu à l’intérieur intact (ils ne sont plus à l’intérieur d’aucun <ng-template>
comme dans le cas précédent). Ainsi, il rendra le contenu correctement.
Pour en savoir plus sur la façon d’utiliser ce format avec d’autres directives structurelles, référez-vous à cet article.
Méthode 2:
C’est un format assez inédit et rarement utilisé (utilisation de deux <ng-template>
frères et sœurs). Ici, nous donnons à un modèle une référence au *ngIf
dans son then
pour lui indiquer quel modèle doit être utilisé si la condition est vraie.
Utiliser plusieurs <ng-template>
comme cela n’est pas conseillé (vous pourriez utiliser <ng-container>
à la place) car ce n’est pas ce à quoi ils sont destinés. Ils sont utilisés comme un conteneur à des modèles qui peuvent être réutilisés à plusieurs endroits. Nous couvrirons plus sur ce sujet dans une section ultérieure de cet article.
<ng-container>
Avez-vous déjà écrit ou vu du code ressemblant à ceci:
La raison pour laquelle beaucoup d’entre nous écrivent ce code est l’incapacité d’utiliser plusieurs directives structurelles sur un seul élément hôte dans Angular. Maintenant, ce code fonctionne bien mais il introduit plusieurs <div>
vides supplémentaires dans le DOM si item.id
est une valeur falsy qui pourrait ne pas être nécessaire.
On peut ne pas être concerné pour un exemple simple comme celui-ci mais pour une application énorme qui a un DOM complexe (pour afficher des dizaines de milliers de données) cela pourrait devenir gênant car les éléments pourraient avoir des écouteurs attachés à eux qui seront toujours là dans le DOM à écouter les événements.
Ce qui est encore pire est le niveau d’imbrication que vous devez faire pour appliquer votre style (CSS) !
Pas d’inquiétude, nous avons <ng-container>
à la rescousse!
Le <ng-container>
d’Angular est un élément de regroupement qui n’interfère pas avec les styles ou la mise en page parce qu’Angular ne le place pas dans le DOM.
Si nous écrivons notre exemple 1 avec <ng-container>
:
Nous obtenons le DOM final comme :
Voyez que nous nous sommes débarrassés de ces <div>
s vides. Nous devrions utiliser <ng-container>
lorsque nous voulons simplement appliquer plusieurs directives structurelles sans introduire d’élément supplémentaire dans notre DOM.
Pour plus d’informations, référez-vous aux docs. Il y a un autre cas d’utilisation où il est utilisé pour injecter un template dynamiquement dans une page. Je couvrirai ce cas d’utilisation dans la dernière section de cet article.
<ng-content>
Ils sont utilisés pour créer des composants configurables. Cela signifie que les composants peuvent être configurés en fonction des besoins de son utilisateur. Ceci est bien connu sous le nom de projection de contenu. Les composants qui sont utilisés dans les bibliothèques publiées font usage de <ng-content>
pour se rendre configurables.
Considérons un simple composant <project-content>
:
Le contenu HTML passé dans les balises d’ouverture et de fermeture du composant <project-content>
est le contenu à projeter. C’est ce que nous appelons la projection de contenu. Le contenu sera rendu à l’intérieur du <ng-content>
dans le composant. Cela permet au consommateur du composant <project-content>
de passer n’importe quel pied de page personnalisé à l’intérieur du composant et de contrôler exactement comment il veut qu’il soit rendu.
Projections multiples :
Et si vous pouviez décider quel contenu doit être placé où ? Au lieu de chaque contenu projeté à l’intérieur d’un seul <ng-content>
, vous pouvez également contrôler comment le contenu sera projeté avec l’attribut select
de <ng-content>
. Il prend un sélecteur d’élément pour décider du contenu à projeter à l’intérieur d’un <ng-content>
particulier.
Voici comment:
Nous avons modifié la définition de <project-content>
pour effectuer une projection multi-contenu. L’attribut select
sélectionne le type de contenu qui sera rendu à l’intérieur d’un <ng-content>
particulier. Ici, nous avons d’abord select
pour rendre l’élément h1
d’en-tête. Si le contenu projeté n’a pas d’élément h1
, il ne rendra rien. De même, le deuxième select
cherche un div
. Le reste du contenu est rendu à l’intérieur du dernier <ng-content>
sans select
.
L’appel du composant ressemblera à :
*ngTemplateOutlet
…Ils sont utilisés comme conteneur de modèles qui peuvent être réutilisés à plusieurs endroits. Nous couvrirons plus sur ce sujet dans une section ultérieure de cet article.
…Il y a un autre cas d’utilisation où il est utilisé pour injecter un modèle dynamiquement dans une page. Je couvrirai ce cas d’utilisation dans la dernière section de cet article.
C’est la section où nous allons discuter des deux points mentionnés précédemment. *ngTemplateOutlet
est utilisé pour deux scénarios – pour insérer un modèle commun dans diverses sections d’une vue indépendamment des boucles ou de la condition et pour faire un composant hautement configuré.
Réutilisation du modèle:
Pensez à une vue où vous devez insérer un modèle à plusieurs endroits. Par exemple, un logo d’entreprise à placer au sein d’un site web. Nous pouvons le réaliser en écrivant le modèle pour le logo une fois et en le réutilisant partout dans la vue.
Suivant est l’extrait de code:
Comme vous pouvez le voir, nous avons juste écrit le modèle de logo une fois et l’avons utilisé trois fois sur la même page avec une seule ligne de code!
*ngTemplateOutlet
accepte également un objet de contexte qui peut être passé pour personnaliser la sortie du modèle commun. Pour plus d’informations sur l’objet de contexte se référer à la docs officielle.
Composants personnalisables:
Le deuxième cas d’utilisation de *ngTemplateOutlet
est les composants hautement personnalisés. Considérez notre exemple précédent de <project-content>
composant avec quelques modifications:
Ci-haut est la version modifiée du composant <project-content>
qui accepte trois propriétés d’entrée – headerTemplate
, bodyTemplate
, footerTemplate
. Voici l’extrait pour project-content.ts
:
Ce que nous essayons de réaliser ici est d’afficher l’en-tête, le corps et le pied de page tels qu’ils sont reçus du composant parent de <project-content>
. Si l’un d’entre eux n’est pas fourni, notre composant affichera le modèle par défaut à sa place. Ainsi, la création d’un composant hautement personnalisé.
Pour utiliser notre composant récemment modifié:
C’est ainsi que nous allons passer les refs de template à notre composant. Si l’un d’entre eux n’est pas passé alors le composant rendra le modèle par défaut.
ng-content vs. *ngTemplateOutlet
Ils nous aident tous deux à réaliser des composants hautement personnalisés mais lequel choisir et quand ?
On peut clairement voir que *ngTemplateOutlet
nous donne un peu plus de pouvoir pour montrer le modèle par défaut si aucun n’est fourni.
Ce n’est pas le cas avec ng-content
. Il rend le contenu tel quel. Au maximum, vous pouvez diviser le contenu et les rendre à différents endroits de votre vue avec l’aide de l’attribut select
. Vous ne pouvez pas rendre le contenu de manière conditionnelle dans ng-content
. Vous devez montrer le contenu qui est reçu du parent sans aucun moyen de prendre des décisions basées sur le contenu.
Cependant, le choix de sélectionner parmi les deux dépend complètement de votre cas d’utilisation. Au moins maintenant nous avons une nouvelle arme *ngTemplateOutlet
dans notre arsenal qui fournit plus de contrôle sur le contenu en plus des fonctionnalités de ng-content
!
.