Angular NgFor, – le guide complet

Dans ce post, vous allez apprendre à utiliser la directive NgFor d’Angular pour boucler sur des données afin de rendre des données ou des composants. Rendre une liste de <todo-item> composants serait un excellent cas d’utilisation pour NgFor.

Comme Angular est un framework réactif, il est courant de voir NgFor être utilisé aux côtés d’observables, et donc nos exemples de code suivront également un style réactif. NgFor supporte également les tableaux et les objets de type tableau – nous explorerons chaque approche.

Qu’est-ce que NgFor?

NgFor est l’une des directives Angular les plus couramment utilisées qui est fournie avec le module commun d’Angular.

🙌 Conseil : incluez la BrowserModule dans le module racine de votre application, car elle inclut déjà la CommonModule pour nous !

NgFor nous permet de boucler sur les données et d’accéder à chaque value et index – un peu comme un Array ForEach ordinaire.

La directive NgFor fait également bien plus que simplement boucler et nous donner une valeur et un index, elle peut être combinée avec des observables via le tuyau async ou améliorer nos performances de rendu avec la fonction trackBy que nous pouvons fournir.

Pour cet article, nous allons inclure un autre composant ContactCardComponent dans notre @NgModule :

Notre ContactCardComponent prend un seul @Input de contact:

Alors maintenant que nous sommes tous configurés, quelle est la suite ?

Itération des collections

Maintenant que notre ContactCardComponent est inclus dans notre module, nous pouvons configurer notre AppComponent pour utiliser ce jeu de données :

Comme mentionné dans l’introduction, j’utilise ici Observable.of de RxJS pour me donner un flux Observable à partir des résultats, c’est une belle façon d’imiter une réponse Observable, comme lors de l’utilisation du module HttpClient d’Angular pour retourner les données d’une API.

Pour la pratique

Maintenant que nous sommes configurés, nous pouvons nous pencher sur notre AppComponent template :

@Component({ selector: 'app-root', template: ` <div class="app"> <ul> <li> <contact-card></contact-card> </li> </ul> </div> `})

Vous pouvez voir que je déclare <contact-card> à l’intérieur d’ici, car nous voulons itérer notre ensemble de données et alimenter chaque contact via la @Input configurée à l’intérieur de notre ContactCardComponent.

Une façon de le faire est d’utiliser ngFor sur le composant lui-même, cependant pour la simplicité nous allons utiliser la liste non ordonnée. Ajoutons ngFor :

<ul> <li *ngFor="let contact of contacts"> <contact-card></contact-card> </li></ul>

Il se passe plusieurs choses ici, la première vous remarquerez un caractère * au début de la ngFor, nous verrons ce que cela signifie dans la prochaine section, lorsque nous examinerons l’élément <ng-template>. Deuxièmement, nous créons un contexte appelé contact, en utilisant une boucle « for of ».

La directive ngFor clonera le <li> et les nœuds enfants. Dans ce cas, la <contact-card> est un nœud enfant, et une carte sera  » estampillée  » dans le DOM pour chaque élément particulier à l’intérieur de notre collection contacts.

🎉 Téléchargez-le gratuitement !

Allez au-delà de Array ForEach. Mettez-vous en confiance avec des méthodes plus avancées comme Reduce, Find, Filter, Every, Some et Map et comprenez pleinement comment gérer les structures de données JavaScript.

  • Comprenez pleinement comment gérer les structures de données JavaScript avec des opérations immuables
  • 31 pages de syntaxe en profondeur, des exemples réels, des conseils et des astuces
  • Écrire une logique de programmation plus propre et mieux structurée en 3 heures

✅ Succès ! Vérifiez votre courriel, profitez-en.

En prime, nous vous enverrons également quelques goodies supplémentaires à travers quelques emails supplémentaires.

Alors, maintenant que nous avons contact disponible en tant qu’objet individuel, nous pouvons passer les contact individuels dans le «  :

<ul> <li *ngFor="let contact of contacts"> <contact-card ="contact"></contact-card> </li></ul>

Si vous utilisez un tableau statique, ou si vous liez le résultat d’un Observable au modèle, vous pouvez laisser le modèle tel qu’il est actuellement. Cependant, nous pouvons facultativement lier l’Observable directement au modèle, ce qui signifie que nous aurons besoin du tuyau async ici pour terminer les choses :

<ul> <li *ngFor="let contact of contacts | async"> <contact-card ="contact"></contact-card> </li></ul>

Utilisation de trackBy pour les clés

Si vous venez d’un contexte AngularJS, vous aurez probablement vu  » track by  » lors de l’utilisation d’un ng-repeat, et de la même manière au pays de React, en utilisant key sur un élément de collection.

Donc, que font ces éléments ? Ils associent les objets, ou clés, aux nœuds DOM particuliers, donc si quelque chose change ou doit être rendu à nouveau, le framework peut le faire beaucoup plus efficacement. La ngFor d’Angular utilise par défaut la vérification de l’identité des objets pour vous, ce qui est rapide, mais peut être plus rapide !

C’est là que trackBy entre en jeu, ajoutons un peu plus de code puis expliquons :

<ul> <li *ngFor="let contact of contacts | async; trackBy: trackById;"> <contact-card ="contact"></contact-card> </li></ul>

Ici nous avons ajouté trackBy, puis lui avons donné une valeur de trackById. Voici une fonction que nous ajouterons dans la classe du composant :

trackById(index, contact) { return contact.id;}

Tout ce que fait cette fonction, c’est utiliser une solution de suivi personnalisée pour notre collection. Au lieu d’utiliser l’identité des objets, nous disons ici à Angular d’utiliser la propriété unique id que chaque contact objet contient. En option, nous pouvons utiliser la propriété index (qui est l’indice dans la collection de chaque élément, c’est-à-dire 0, 1, 2, 3, 4).

Si votre API renvoie des données uniques, alors utiliser cela serait une solution préférable à index – car le index peut être sujet à changement si vous réorganisez votre collection. L’utilisation d’un identifiant unique permet à Angular de localiser ce nœud DOM associé à l’objet beaucoup plus rapidement, et il réutilisera le composant dans le DOM s’il doit être mis à jour – au lieu de le détruire et de le reconstruire.

Capturer « index » et « count »

La directive ngFor ne s’arrête pas à l’itération, elle nous fournit également quelques autres subtilités. Explorons index et count, deux propriétés publiques qui nous sont exposées à chaque ngFor itération.

Créons une autre variable appelée i, à laquelle nous affecterons la valeur de index. Angular expose ces valeurs sous le capot pour nous, et lorsque nous regardons la section suivante avec l’élément <ng-template>, nous pouvons voir comment elles sont composées.

Pour consigner l’index, nous pouvons simplement interpoler i:

<ul> <li *ngFor="let contact of contacts | async; index as i;"> Index: {{ i }} <contact-card ="contact"></contact-card> </li></ul>

Cela nous donnera chaque index, à partir de 0, pour chaque élément de notre collection. Exposons également count:

La count retournera une longueur de collection vivante, équivalente à contacts.length. Ceux-ci peuvent éventuellement être liés et passés dans chaque composant, par exemple, vous pouvez souhaiter enregistrer la longueur totale de votre collection quelque part, et également passer la index du contact particulier dans une fonction @Output :

Accéder au premier, dernier, impair, pair

Quatre autres propriétés exposées par ngFor (en fait, en dessous, elle utilise NgForOfContext, une classe qui génère chaque contexte ngFor en interne). Examinons rapidement le code source à ce sujet :

Comme je l’ai mentionné plus haut, la NgForOfContext est ce qui construit nos éléments ngFor, et vous pouvez voir dans la constructor que nous avons déjà jeté un coup d’œil à index et count ! Les dernières choses que nous devons examiner sont les getters, que nous pouvons expliquer à partir du code source ci-dessus :

  • premier : renvoie true pour le premier élément de la collection, fait correspondre l’index avec zéro
  • dernier : renvoie true pour le dernier élément de la collection, correspond à l’index avec le compte total, moins un pour décaler le « compte » vers le bas d’un pour tenir compte des index basés sur zéro
  • même : renvoie true pour les éléments pairs (par ex.p. ex. 2, 4) dans la collection, utilise % l’opérateur modulus pour calculer en fonction de l’index
  • odd : renvoie true pour les éléments impairs (p. ex. 1, 3), inverse simplement this.even résultat

En utilisant ceci, nous pouvons ajouter conditionnellement des choses telles que le style, ou accrocher dans la propriété last pour savoir quand la collection a terminé le rendu.

Pour cette rapide démonstration, nous utiliserons ngClass pour ajouter quelques styles à chaque <li> (notez comment nous créons plus de variables, tout comme index) :

Et quelques styles :

Nous ne ferons pas la démonstration de first et last, car il est assez évident d’après ce qui précède comment nous pouvons les brancher !

élément

Nous avons mentionné plus tôt dans cet article que nous allions chercher à comprendre ce que signifiait le * dans nos modèles. Cela partage également la même syntaxe que *ngIf, que vous avez probablement aussi vu auparavant.

Donc, dans cette prochaine section, nous allons plonger plus profondément sur ngFor* et l’élément <ng-template> pour expliquer plus en détail ce qui se passe réellement ici.

Lorsque nous utilisons un astérisque (*) dans nos modèles, nous informons Angular que nous utilisons une directive structurelle, qui est également une syntaxe sugar (un raccourci agréable) pour utiliser l’élément <ng-template>.

et les composants Web

Alors, qu’est-ce que l’élément <ng-template> ? Tout d’abord, faisons un pas en arrière. Nous allons revenir en arrière pour montrer un peu de code AngularJS ici, peut-être avez-vous déjà fait cela ou fait quelque chose de similaire dans un autre framework/librairie :

<script type="text/ng-template"> <div> My awesome template! </div></script>

Ceci remplace le type sur la balise <script>, qui empêche le moteur JavaScript d’analyser le contenu de la balise <script>. Cela nous permet, ou à un framework tel qu’AngularJS, de récupérer le contenu de la balise script et de l’utiliser comme une forme de modèle HTML.

Web Components a introduit il y a quelques années une nouvelle spécification similaire à cette idée, appelée <template> :

<template> <div> My awesome template! </div></template>

Pour saisir notre modèle ci-dessus et l’instancier, nous ferions ceci en JavaScript simple :

Notez comment nous avons id=host, qui est notre Node « hôte » pour que le modèle soit injecté dans.

Vous avez peut-être vu ce terme flotter dans Angular de quelques façons, comme les préfixes _nghost sur les Nodes (ng-host) ou la propriété host dans les directives.

ngFor et ng-template

Tout d’abord, <ng-template> est la propre implémentation d’Angular de la balise <template>, nous permettant de penser à la conception d’applications en composants web et aux idées qui les sous-tendent. Il nous offre également plus de puissance que ce que l’élément <template> nous donne par défaut, en s’intégrant de manière transparente dans la façon dont Angular compile notre code.

Alors, en quoi l’explication ci-dessus de <template> nous en dit plus sur ngFor et la * ? L’astérisque est une syntaxe abrégée pour utiliser l’élément <ng-template>.

Partons de l’exemple de base ngFor:

<ul> <li *ngFor="let contact of contacts | async"> <contact-card ="contact"></contact-card> </li></ul>

Et démontrons l’équivalent <ng-template> :

<ul> <ng-template ngFor let-contact ="contacts | async"> <li> <contact-card ="contact"></contact-card> </li> </ng-template></ul>

C’est très différent ! Que se passe-t-il ici ?

Lorsque nous utilisons *ngFor, nous demandons à Angular de traiter essentiellement l’élément auquel est lié le * comme un modèle.

L’élément <ng-template> d’Angular n’est pas un véritable composant Web (contrairement à <template>). Il reflète simplement les concepts qui le sous-tendent pour vous permettre d’utiliser <ng-template> comme il est prévu dans la spécification. Lorsque nous compilons notre code (JiT ou AoT), nous ne verrons aucun élément <ng-template> sorti dans le DOM. Cependant, cela ne signifie pas que nous ne pouvons pas utiliser des choses comme Shadow DOM, car elles sont encore tout à fait possibles.

Poursuivons, et comprenons ce que ngForlet-contact et ngForOf font ci-dessus.

For et modèles de vue intégrés

Première chose, ngFor est une directive ! Vérifions une partie du code source :

@Directive({selector: ''})export class NgForOf<T, U extends NgIterable<T> = NgIterable<T>> implements DoCheck {...}

Ici, Angular utilise des sélecteurs d’attributs comme valeur de selector pour indiquer au décorateur @Directive quels attributs rechercher.

La directive utilise , ce qui implique qu’il y a deux attributs comme sélecteur enchaîné. Alors, comment fonctionne ngFor si nous n’utilisons pas ngForOf ?

Le compilateur d’Angular transforme tous les éléments <ng-template> et les directives utilisées avec un astérisque (*) en vues distinctes de la vue du composant racine. Ceci afin que chaque vue puisse être créée plusieurs fois.

Pendant la phase de compilation, il prendra let contact of contacts et mettra en majuscule la of, et créera une clé personnalisée pour créer ngForOf.

Dans notre cas, Angular va construire une vue qui crée tout depuis la balise <li> vers l’intérieur :

<!-- view --><li> <contact-card ="contact"></contact-card></li><!-- /view -->

Il crée également un conteneur de vue invisible pour contenir toutes les instances du modèle, agissant comme un placeholder pour le contenu. Le conteneur de vue qu’Angular a créé enveloppe essentiellement les  » vues « , dans notre cas, c’est juste à l’intérieur des balises <ul>. Cela abrite tous les modèles qui sont créés par ngFor (un pour chaque ligne).

Un pseudo-sortie pourrait ressembler à ceci :

ngFor crée une « vue embarquée » pour chaque ligne, en passant par la vue qu’elle a créée et le contexte de la ligne (l’index et les données de la ligne). Cette vue embarquée est ensuite insérée dans le conteneur de la vue. Lorsque les données changent, elle suit les éléments pour voir s’ils ont été déplacés. S’ils ont bougé, au lieu de recréer les vues embarquées, il les déplace pour être à la bonne position, ou les détruit s’ils n’existent plus.

Contexte et passage de variables

L’étape suivante consiste à comprendre comment Angular passe le contexte à chaque <contact-card> :

<ng-template ngFor let-contact ="contacts | async"> <li> <contact-card ="contact"></contact-card> </li></ng-template>

Alors maintenant que nous avons compris ngFor et ngForOf, comment Angular associe-t-il let-contact à l’individuelle contact à laquelle nous lions ensuite la propriété ?

Parce que let-contact n’a pas de valeur, c’est simplement un attribut, c’est ici qu’Angular fournit une valeur  » implicite « , ou $implicit comme on l’appelle sous le capot.

Alors qu’Angular crée chaque ngFor élément, il utilise une classe NgForOfContext aux côtés d’une EmbeddedViewRef, et transmet ces propriétés de manière dynamique. Voici un petit extrait du code source :

A côté de cette section de code, nous pouvons également voir comment nos propriétés index et count susmentionnées sont maintenues à jour :

Vous pouvez creuser le code source de la directive de manière plus détaillée ici.

Voici comment nous pouvons ensuite accéder aux index et count comme ceci :

Notez comment nous fournissons les valeurs let-i et let-c qui sont exposées depuis l’instance NgForRow, contrairement à let-contact.

Pour apprendre plus de techniques, de meilleures pratiques et de connaissances d’experts du monde réel, je vous recommande vivement de consulter mes cours Angular – ils vous guideront tout au long de votre parcours pour maîtriser Angular au maximum !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *