Angular NgFor, – der komplette Leitfaden

In diesem Beitrag lernen Sie, wie Sie die NgFor-Direktive von Angular verwenden können, um Daten oder Komponenten in einer Schleife darzustellen. Das Rendern einer Liste von <todo-item>-Komponenten wäre ein großartiger Anwendungsfall für NgFor.

Da Angular ein reaktives Framework ist, ist es üblich, NgFor zusammen mit Observables zu verwenden, und so werden unsere Codebeispiele auch einem reaktiven Stil folgen. NgFor unterstützt auch Arrays und Array-ähnliche Objekte – wir werden jeden Ansatz untersuchen.

Was ist NgFor?

NgFor ist eine der am häufigsten verwendeten Angular-Direktiven, die mit dem CommonModule von Angular geliefert wird.

🙌 Tipp: Fügen Sie das BrowserModule in das Root-Modul Ihrer App ein, da es das CommonModule bereits für uns enthält!

NgFor erlaubt es uns, eine Schleife über die Daten zu ziehen und auf jedes value und index zuzugreifen – ähnlich wie ein normales Array ForEach.

Die NgFor-Direktive macht auch weit mehr als nur eine Schleife und gibt uns einen Wert und einen Index, sie kann mit Observables über die async-Pipe kombiniert werden oder unsere Rendering-Performance mit der trackBy-Funktion verbessern, die wir anbieten können.

Für diesen Artikel werden wir eine weitere ContactCardComponent-Komponente in unser @NgModule einbinden:

Unser ContactCardComponent nimmt ein einzelnes @Input von contact auf:

So, jetzt sind wir alle eingerichtet, was kommt jetzt?

Iterieren von Sammlungen

Nun, da unser ContactCardComponent in unserem Modul enthalten ist, können wir unser AppComponent so einrichten, dass es dieses Dataset verwendet:

Wie in der Einleitung erwähnt, verwende ich hier Observable.of von RxJS, um mir einen Observable-Stream aus den Ergebnissen zu liefern; dies ist ein schöner Weg, um eine Observable-Antwort zu imitieren, etwa wenn man das HttpClient-Modul von Angular verwendet, um Daten von einer API zurückzugeben.

Angular in der Praxis

Nachdem wir nun eingerichtet sind, können wir einen Blick in unser AppComponent-Template werfen:

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

Sie können sehen, dass ich hier <contact-card> deklariere, da wir unseren Datensatz iterieren und jeden Kontakt über das @Input innerhalb unseres ContactCardComponent auffüllen wollen.

Eine Möglichkeit, dies zu tun, ist die Verwendung von ngFor in der Komponente selbst, der Einfachheit halber verwenden wir jedoch die ungeordnete Liste. Fügen wir also ngFor ein:

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

Hier passieren ein paar Dinge, als erstes fällt Ihnen ein * Zeichen am Anfang des ngFor auf, was das bedeutet, erfahren wir im nächsten Abschnitt, wenn wir uns das <ng-template>-Element ansehen. Zweitens erstellen wir einen Kontext namens contact, indem wir eine „for of“-Schleife verwenden.

Die ngFor-Direktive klont das <li> und die Kindknoten. In diesem Fall ist das <contact-card> ein Kindknoten, und für jedes einzelne Element innerhalb unserer contacts-Sammlung wird eine Karte im DOM „ausgestanzt“.

🎉 Download it free!

Gehen Sie über Array ForEach hinaus. Werden Sie sicher im Umgang mit fortgeschrittenen Methoden wie Reduce, Find, Filter, Every, Some und Map und verstehen Sie, wie Sie JavaScript-Datenstrukturen verwalten.

  • Verstehen Sie vollständig, wie man JavaScript-Datenstrukturen mit unveränderlichen Operationen verwaltet
  • 31 Seiten mit tiefgründiger Syntax, Beispiele aus der Praxis, Tipps und Tricks
  • Schreiben Sie sauberere und besser strukturierte Programmierlogik innerhalb von 3 Stunden

✅ Erfolg! Checken Sie Ihre E-Mail, viel Spaß!

Als Extra-Bonus schicken wir Ihnen außerdem ein paar zusätzliche Goodies über ein paar zusätzliche E-Mails.

So, jetzt haben wir contact als einzelnes Objekt zur Verfügung, wir können das einzelne contact in die „ übergeben:

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

Wenn Sie ein statisches Array verwenden oder das Ergebnis einer Observable an das Template binden, können Sie das Template so lassen, wie es ist. Wir können jedoch optional das Observable direkt an das Template binden, was bedeutet, dass wir hier die async-Pipe benötigen, um die Sache abzuschließen:

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

Using trackBy for keys

Wenn Sie von einem AngularJS-Hintergrund kommen, werden Sie wahrscheinlich „trackBy“ gesehen haben, wenn Sie ein ng-repeat verwenden, und ähnlich im React-Land, wenn Sie key für ein Collection-Element verwenden.

Was bewirken diese also? Sie assoziieren die Objekte, oder Schlüssel, mit den jeweiligen DOM-Knoten, so dass, sollte sich etwas ändern oder neu gerendert werden müssen, das Framework dies viel effizienter erledigen kann. Angulars ngFor verwendet standardmäßig die Objektidentitätsprüfung für Sie, was schnell ist, aber noch schneller sein kann!

Hier kommt trackBy ins Spiel, fügen wir etwas mehr Code hinzu und erklären dann:

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

Hier haben wir trackBy hinzugefügt und ihm dann den Wert trackById gegeben. Dies ist eine Funktion, die wir in der Komponentenklasse hinzufügen werden:

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

Alles, was diese Funktion tut, ist die Verwendung einer benutzerdefinierten Tracking-Lösung für unsere Sammlung. Anstatt die Objektidentität zu verwenden, weisen wir Angular hier an, die eindeutige id-Eigenschaft zu verwenden, die jedes contact-Objekt enthält. Optional können wir auch das index verwenden (das ist der Index in der Sammlung jedes Objekts, also 0, 1, 2, 3, 4).

Wenn Ihre API eindeutige Daten zurückgibt, dann wäre die Verwendung dieses Bezeichners die bessere Lösung als index – da sich das index ändern kann, wenn Sie Ihre Sammlung neu anordnen. Die Verwendung eines eindeutigen Bezeichners ermöglicht es Angular, den mit dem Objekt verbundenen DOM-Knoten viel schneller zu finden, und es wird die Komponente im DOM wiederverwenden, wenn sie aktualisiert werden muss – anstatt sie zu zerstören und neu aufzubauen.

Erfassen von „index“ und „count“

Die ngFor-Direktive hört nicht nur bei der Iteration auf, sie bietet uns auch ein paar andere Feinheiten. Schauen wir uns index und count an, zwei öffentliche Eigenschaften, die uns bei jeder ngFor-Iteration zur Verfügung stehen.

Lassen Sie uns eine weitere Variable namens i erstellen, der wir den Wert von index zuweisen werden. Angular legt diese Werte unter der Haube für uns offen, und wenn wir uns den nächsten Abschnitt mit dem <ng-template>-Element ansehen, können wir sehen, wie sie zusammengesetzt sind.

Um den Index auszuloggen, können wir einfach i interpolieren:

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

Damit erhalten wir jeden Index, beginnend mit 0, für jedes Element in unserer Sammlung. Lassen Sie uns auch count ausgeben:

Das count wird eine Live-Sammlungslänge zurückgeben, die contacts.length entspricht. Diese kann optional gebunden und in jede Komponente übergeben werden, z.B. können Sie die Gesamtlänge Ihrer Sammlung irgendwo ausloggen, und auch das index des jeweiligen Kontakts in eine Funktion @Output übergeben:

Zugriff auf erste, letzte, ungerade, gerade

Vier weitere Eigenschaften, die von ngFor offengelegt werden (naja, eigentlich wird darunter NgForOfContext verwendet, eine Klasse, die jeden ngFor-Kontext intern erzeugt). Schauen wir uns kurz den Quellcode dazu an:

Wie ich oben schon erwähnt habe, ist das NgForOfContext das, was unsere ngFor Elemente konstruiert, und Sie können im constructor sehen, dass wir bereits einen Blick auf index und count geworfen haben! Die letzten Dinge, die wir uns ansehen müssen, sind die Getter, die wir anhand des obigen Quellcodes erklären können:

  • first: liefert true für das erste Element in der Sammlung, entspricht dem Index mit Null
  • last: Gibt true für das letzte Element in der Sammlung zurück, entspricht dem Index mit der Gesamtzahl, minus eins, um die „Anzahl“ um eins nach unten zu verschieben, um null-basierte Indizes zu berücksichtigen
  • even: Gibt true für gerade Elemente (z.z. B. 2, 4) in der Sammlung, verwendet % Modulus-Operator zur Berechnung auf Basis des Index
  • ungerade: liefert true für ungerade Elemente (z. B. 1, 3), invertiert einfach das this.even Ergebnis

Damit können wir Dinge wie Styling bedingt anwenden oder die Eigenschaft last einbinden, um zu wissen, wann die Sammlung fertig gerendert ist.

Für diese kurze Demo verwenden wir ngClass, um einige Stile zu jedem <li> hinzuzufügen (beachten Sie, wie wir weitere Variablen erstellen, genau wie index):

Und einige Stile:

Wir werden first und last nicht demonstrieren, da es aus dem obigen ziemlich offensichtlich ist, wie wir diese einbinden können!

Element

Wir haben weiter oben in diesem Artikel erwähnt, dass wir uns ansehen werden, was das * in unseren Vorlagen bedeutet. Dieses hat die gleiche Syntax wie *ngIf, das Sie wahrscheinlich auch schon gesehen haben.

Im nächsten Abschnitt gehen wir also tiefer auf ngFor* und das <ng-template>-Element ein, um genauer zu erklären, was hier wirklich passiert.

Wenn wir ein Sternchen (*) in unseren Templates verwenden, teilen wir Angular mit, dass wir eine strukturelle Direktive verwenden, was auch eine Zuckersyntax (eine schöne Abkürzung) für die Verwendung des <ng-template>-Elements ist.

und Webkomponenten

So, was ist das <ng-template>-Element? Lassen Sie uns zunächst einen Schritt zurückgehen. Wir gehen zurück und zeigen hier etwas AngularJS-Code, vielleicht haben Sie das schon einmal gemacht oder etwas Ähnliches in einem anderen Framework/einer anderen Bibliothek getan:

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

Dies setzt das type auf dem <script>-Tag außer Kraft, was die JavaScript-Engine daran hindert, den Inhalt des <script>-Tags zu parsen. Dies erlaubt uns oder einem Framework wie AngularJS, den Inhalt des Skript-Tags zu holen und ihn als eine Art HTML-Vorlage zu verwenden.

Web Components hat vor ein paar Jahren eine neue Spezifikation eingeführt, die dieser Idee ähnlich ist und <template> heißt:

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

Um unser obiges Template zu nehmen und zu instanziieren, würden wir dies in einfachem JavaScript tun:

Beachten Sie, dass wir id=host haben, was unser „Host“-Knoten ist, in den das Template injiziert werden soll.

Sie haben diesen Begriff vielleicht schon einmal in Angular gesehen, zum Beispiel als _nghost-Präfixe auf Nodes (ng-host) oder die host-Eigenschaft in Direktiven.

ngFor und ng-template

Zunächst einmal ist <ng-template> die Angular-eigene Implementierung des <template>-Tags, die es uns ermöglicht, das Anwendungsdesign in Web-Komponenten und die Ideen dahinter zu denken. Es bietet uns auch mehr Möglichkeiten, als das <template>-Element uns standardmäßig bietet, und fügt sich nahtlos in die Art und Weise ein, wie Angular unseren Code kompiliert.

So, wie sagt uns die obige <template>-Erklärung mehr über ngFor und das *? Das Sternchen ist eine Kurzsyntax für die Verwendung des <ng-template>-Elements.

Lassen Sie uns mit dem einfachen ngFor-Beispiel beginnen:

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

Und demonstrieren Sie das <ng-template>-Äquivalent:

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

Das ist ganz anders! Was passiert hier?

Wenn wir *ngFor verwenden, weisen wir Angular an, das Element, an das das * gebunden ist, als Template zu behandeln.

Angulars <ng-template>-Element ist keine echte Web-Komponente (im Gegensatz zu <template>). Es spiegelt lediglich die Konzepte dahinter wider, damit Sie <ng-template> so verwenden können, wie es in der Spezifikation vorgesehen ist. Wenn wir unseren Code kompilieren (JiT oder AoT), werden wir keine <ng-template> Elemente im DOM ausgegeben sehen. Das bedeutet jedoch nicht, dass wir Dinge wie Shadow DOM nicht verwenden können, da sie immer noch vollständig möglich sind.

Lassen Sie uns fortfahren und verstehen, was ngForlet-contact und ngForOf oben tun.

ngFor und eingebettete View-Templates

Zunächst einmal: ngFor ist eine Direktive! Schauen wir uns einen Teil des Quellcodes an:

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

Hier verwendet Angular Attributselektoren als Wert von selector, um dem @Directive-Dekorator mitzuteilen, nach welchen Attributen er suchen soll.

Die Direktive verwendet , was impliziert, dass es zwei Attribute als verketteten Selektor gibt. Wie funktioniert also ngFor, wenn wir nicht ngForOf verwenden?

Angulars Compiler wandelt alle <ng-template>-Elemente und Direktiven, die mit einem Sternchen (*) verwendet werden, in Ansichten um, die von der Stammkomponentenansicht getrennt sind. So kann jede Ansicht mehrfach erstellt werden.

Während der Kompilierungsphase wird let contact of contacts genommen und das of großgeschrieben, und ein benutzerdefinierter Schlüssel erstellt, um ngForOf zu erstellen.

In unserem Fall wird Angular eine Ansicht konstruieren, die alles ab dem <li>-Tag nach innen erzeugt:

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

Es wird auch ein unsichtbarer View-Container erstellt, der alle Instanzen des Templates enthält und als Platzhalter für Inhalte dient. Der von Angular erstellte View-Container umhüllt im Wesentlichen die „Views“, in unserem Fall also innerhalb des <ul>-Tags. Dieser beherbergt alle Templates, die von ngFor erstellt werden (eines für jede Zeile).

Eine Pseudo-Ausgabe könnte so aussehen:

ngFor erstellt für jede Zeile eine „eingebettete Ansicht“, die den von ihr erstellten View und den Kontext der Zeile (den Index und die Zeilendaten) durchreicht. Diese eingebettete Ansicht wird dann in den Ansichts-Container eingefügt. Wenn sich die Daten ändern, verfolgt es die Elemente, um zu sehen, ob sie verschoben wurden. Wenn sie verschoben wurden, werden die eingebetteten Ansichten nicht neu erstellt, sondern an die richtige Position verschoben oder zerstört, wenn sie nicht mehr vorhanden sind.

Kontext und Übergabe von Variablen

Der nächste Schritt besteht darin zu verstehen, wie Angular den Kontext an jedes <contact-card> übergibt:

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

So, jetzt haben wir ngFor und ngForOf verstanden, wie verknüpft Angular let-contact mit dem einzelnen contact, an das wir dann die Eigenschaften binden?

Da let-contact keinen Wert hat, sondern lediglich ein Attribut ist, stellt Angular hier einen „impliziten“ Wert zur Verfügung, oder $implicit, wie es unter der Haube heißt.

Während Angular jedes ngFor Element erstellt, verwendet es eine NgForOfContext Klasse neben einem EmbeddedViewRef und übergibt diese Eigenschaften dynamisch. Hier ein kleiner Ausschnitt aus dem Quellcode:

Neben diesem Codeabschnitt können wir auch sehen, wie unsere bereits erwähnten index und count Eigenschaften auf dem neuesten Stand gehalten werden:

Sie können sich hier den Quellcode der Direktive genauer ansehen.

So können wir dann auf das index und count zugreifen:

Beachten Sie, dass wir let-i und let-c Werte liefern, die im Gegensatz zu let-contact von der NgForRow-Instanz ausgegeben werden.

Um mehr Techniken, Best Practices und Expertenwissen aus der Praxis zu lernen, empfehle ich Ihnen meine Angular-Kurse – sie werden Sie auf Ihrer Reise zum Beherrschen von Angular in vollem Umfang begleiten!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.