Angular NgFor, – de complete gids

In deze post ga je leren hoe je Angular’s NgFor directive kunt gebruiken om over data te lussen om data of componenten te renderen. Het renderen van een lijst van <todo-item> componenten zou een geweldige use-case zijn voor NgFor.

Aangezien Angular een reactief framework is, zie je vaak dat NgFor wordt gebruikt naast observables, en dus zullen onze code voorbeelden ook een reactieve stijl volgen. NgFor ondersteunt ook arrays en array-achtige objecten – we zullen elke benadering verkennen.

Wat is NgFor?

NgFor is een van de meest gebruikte Angular directives die wordt geleverd met Angular’s CommonModule.

🙌 Tip: Neem de BrowserModule op in de root module van je app, omdat deze de CommonModule al voor ons bevat!

NgFor stelt ons in staat om over gegevens te lussen en toegang te krijgen tot elke value en index – ongeveer zoals een gewone Array ForEach.

De NgFor directive doet ook veel meer dan alleen maar een lus maken en ons een waarde en index geven, hij kan worden gecombineerd met observables via de async pipe of onze renderprestaties verbeteren met de trackBy functie die we kunnen bieden.

Voor dit artikel nemen we nog een ContactCardComponent component op in onze @NgModule:

Onze ContactCardComponent neemt een enkele @Input van contact:

Dus nu zijn we allemaal ingesteld, wat nu?

Itereren van collecties

Nu onze ContactCardComponent in onze module is opgenomen, kunnen we onze AppComponent instellen om deze dataset te gebruiken:

Zoals in de inleiding vermeld, gebruik ik hier Observable.of van RxJS om mij een Observable stream van de resultaten te geven, dit is een mooie manier om een Observable response na te bootsen, zoals bij het gebruik van Angular’s HttpClient module om data van een API te retourneren.

Voor in de praktijk

Nu we zijn ingesteld, kunnen we kijken naar onze AppComponent template:

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

Je kunt zien dat ik <contact-card> hierbinnen declareer, omdat we onze dataset willen itereren en elk contact via de @Input setup binnen onze ContactCardComponent willen vullen.

Een manier om dit te doen is door ngFor op de component zelf te gebruiken, maar voor de eenvoud zullen we de ongeordende lijst gebruiken. Laten we ngFor toevoegen:

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

Er gebeuren hier een paar dingen, als eerste ziet u een * teken aan het begin van de ngFor, we zullen zien wat dit betekent in de volgende sectie wanneer we kijken naar het <ng-template> element. Ten tweede maken we een context genaamd contact, met behulp van een “for of”-lus.

De ngFor-richtlijn zal de <li> en de kind-nodes klonen. In dit geval is de <contact-card> een kind node, en een kaart zal worden “uitgestempeld” in het DOM voor elk specifiek item binnen onze contacts collectie.

🎉 Download het gratis!

Ga verder dan Array ForEach. Raak vertrouwd met meer geavanceerde methoden zoals Reduce, Find, Filter, Every, Some en Map en begrijp volledig hoe je JavaScript Data Structuren moet beheren.

  • Begrijp volledig hoe je JavaScript Data Structures kunt beheren met onveranderlijke bewerkingen
  • 31 pagina’s met diepgaande syntaxis, voorbeelden uit de praktijk, tips en trucs
  • Schrijf schonere en beter gestructureerde programmeerlogica binnen 3 uur

✅ Succes! Controleer uw e-mail, geniet ervan.

Als extra bonus sturen we u ook nog wat extra goodies via een paar extra e-mails.

Dus, nu we contact beschikbaar hebben als een individueel Object, kunnen we de individuele contact doorgeven in de “:

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

Als je een statische array gebruikt, of het resultaat van een Observable aan het sjabloon bindt, kun je het sjabloon laten zoals het nu is. We kunnen echter optioneel de Observable direct aan het sjabloon binden, wat betekent dat we de async pipe hier nodig hebben om de zaken af te ronden:

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

TrackBy gebruiken voor sleutels

Als je een AngularJS-achtergrond hebt, zult u waarschijnlijk “track by” hebben gezien bij het gebruik van een ng-repeat, en op vergelijkbare wijze in React-land, bij het gebruik van key op een collectie-item.

Dus wat doen deze? Ze associëren de objecten, of sleutels, met de specifieke DOM nodes, dus mocht er iets veranderen of opnieuw gerenderd moeten worden, dan kan het framework dit veel efficiënter doen. Angular’s ngFor gebruikt standaard object identiteitscontrole voor je, wat snel is, maar het kan sneller!

Dit is waar trackBy in het spel komt, laten we wat meer code toevoegen en het dan uitleggen:

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

Hier hebben we trackBy toegevoegd, en het vervolgens een waarde gegeven van trackById. Dit is een functie die we in de componentklasse zullen toevoegen:

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

Alles wat deze functie doet, is een aangepaste tracking-oplossing voor onze verzameling gebruiken. In plaats van objectidentiteit te gebruiken, vertellen we Angular hier om de unieke id eigenschap te gebruiken die elk contact object bevat. Optioneel kunnen we de index gebruiken (dat is de index in de collectie van elk item, dus 0, 1, 2, 3, 4).

Als je API unieke gegevens retourneert, dan is dat een betere oplossing dan index – omdat de index onderhevig kan zijn aan verandering als je je verzameling opnieuw ordent. Door een unieke identifier te gebruiken kan Angular veel sneller de DOM-node vinden die bij het object hoort, en het zal de component in het DOM hergebruiken mocht het moeten worden bijgewerkt – in plaats van het te vernietigen en opnieuw te bouwen.

Het vastleggen van “index” en “count”

De ngFor directive stopt niet alleen bij iteratie, het biedt ons ook een paar andere finesses. Laten we index en count eens nader bekijken, twee openbare eigenschappen die bij elke ngFor iteratie aan ons worden blootgesteld.

Laten we nog een variabele aanmaken, i, waaraan we de waarde van index zullen toewijzen. Angular stelt deze waarden onder-de-hood voor ons bloot, en als we naar het volgende gedeelte kijken met het <ng-template> element, kunnen we zien hoe ze zijn samengesteld.

Om de index uit te loggen, kunnen we eenvoudigweg i interpoleren:

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

Dit geeft ons elke index, beginnend bij 0, voor elk item in onze verzameling. Laten we ook count tonen:

De count zal een live collectie lengte teruggeven, equivalent van contacts.length. Deze kunnen optioneel worden gebonden en doorgegeven in elk component, bijvoorbeeld u wilt misschien ergens de totale lengte van uw collectie uitloggen, en ook de index van het specifieke contact doorgeven in een functie @Output:

Toegang tot eerste, laatste, oneven, even

Vier meer eigenschappen die door ngFor worden blootgesteld (nou, eigenlijk gebruikt het daaronder NgForOfContext, een klasse die elke ngFor context intern genereert). Laten we eens snel naar de broncode hiervoor kijken:

Zoals ik hierboven al zei, de NgForOfContext is wat onze ngFor items construeert, en u kunt zien dat we in de constructor al gekeken hebben naar index en count! Het laatste waar we naar moeten kijken zijn de getters, die we kunnen uitleggen aan de hand van de broncode hierboven:

  • first: retourneert true voor het eerste item in de collectie, komt overeen met de index met nul
  • last: retourneert true voor het laatste item in de verzameling, komt overeen met de index met de totale telling, min één om de “telling” één naar beneden te verschuiven om rekening te houden met op nul gebaseerde indexen
  • even: retourneert true voor even items (bijv.bijv. 2, 4) in de verzameling, gebruikt % modulus operator om te berekenen op basis van index
  • oneven: retourneert true voor oneven items (bijv. 1, 3), keert eenvoudig this.even resultaat om

Door dit te gebruiken, kunnen we dingen zoals styling conditioneel toepassen, of inhaken op de last eigenschap om te weten wanneer de collectie klaar is met renderen.

Voor deze snelle demo gebruiken we ngClass om wat stijlen toe te voegen aan elke <li> (merk op hoe we meer variabelen aanmaken, net als index):

En wat stijlen:

We zullen first en last niet demonstreren, omdat het uit het bovenstaande vrij duidelijk is hoe we die aan elkaar kunnen knopen!

element

We zeiden eerder in dit artikel dat we zouden kijken naar wat de * betekende in onze sjablonen. Dit heeft dezelfde syntaxis als *ngIf, die je waarschijnlijk ook al eerder hebt gezien.

Dus in de volgende sectie zullen we dieper ingaan op ngFor* en het <ng-template> element om in meer detail uit te leggen wat hier werkelijk gebeurt.

Wanneer we een sterretje (*) in onze templates gebruiken, laten we Angular weten dat we een structural directive gebruiken, wat ook een suiker syntaxis (een mooie korte hand) is voor het gebruik van het <ng-template> element.

en Web Components

Dus, wat is het <ng-template> element? Laten we eerst een stap terug doen. We gaan terug naar het tonen van wat AngularJS code hier, misschien heb je dit eerder gedaan of iets soortgelijks gedaan in een ander framework/bibliotheek:

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

Dit overschrijft de type op de <script> tag, die voorkomt dat de JavaScript engine de inhoud van de <script> tag parseert. Hierdoor kunnen wij, of een framework zoals AngularJS, de inhoud van de script-tag ophalen en deze gebruiken als een soort HTML-sjabloon.

Web Components heeft een paar jaar geleden een nieuwe spec geïntroduceerd die op dit idee lijkt, genaamd <template>:

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

Om onze bovenstaande sjabloon te pakken en te instantiëren, zouden we dit in gewoon JavaScript doen:

Zie hoe we id=host hebben, dat is onze “host” Node waarin de sjabloon moet worden geïnjecteerd.

Je hebt deze term misschien op een paar manieren in Angular zien rondzweven, zoals _nghost prefixes op Nodes (ng-host) of de host property in directives.

ngFor en ng-template

In de eerste plaats is <ng-template> Angular’s eigen implementatie van de <template> tag, die ons in staat stelt na te denken over applicatieontwerp in webcomponenten en de ideeën erachter. Het biedt ons ook meer kracht dan het <template> element ons standaard geeft, naadloos passend in de manier waarop Angular onze code compileert.

Dus hoe vertelt de bovenstaande <template> uitleg ons meer over ngFor en de *? Het sterretje is een verkorte syntaxis voor het gebruik van het <ng-template> element.

Laten we beginnen met het basisvoorbeeld ngFor:

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

En laten we het <ng-template>-equivalent zien:

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

Dat is heel wat anders! Wat gebeurt hier?

Wanneer we *ngFor gebruiken, vertellen we Angular om het element waaraan de * is gekoppeld in wezen als een sjabloon te behandelen.

Angular’s <ng-template> element is geen echt Web Component (in tegenstelling tot <template>). Het is slechts een afspiegeling van de concepten erachter, zodat je <ng-template> kunt gebruiken zoals het bedoeld is in de spec. Wanneer we onze code (JiT of AoT) compileren, zullen we geen <ng-template> elementen in het DOM zien verschijnen. Dit betekent echter niet dat we geen dingen als Shadow DOM kunnen gebruiken, want die zijn nog steeds volledig mogelijk.

Laten we verder gaan, en begrijpen wat ngForlet-contact en ngForOf hierboven doen.

ngFor en embedded view templates

Eerst belangrijk, ngFor is een directive! Laten we eens wat van de broncode bekijken:

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

Hier gebruikt Angular attribuutselectors als de waarde van selector om de @Directive-decorator te vertellen naar welke attributen hij moet zoeken.

De richtlijn gebruikt , wat impliceert dat er twee attributen zijn als een geketende selector. Dus, hoe werkt ngFor als we ngForOf niet gebruiken?

Angular’s compiler transformeert alle <ng-template> elementen en directives die gebruikt worden met een sterretje (*) in views die gescheiden zijn van de root component view. Dit is zodat elke view meerdere keren kan worden gemaakt.

Tijdens de compileerfase zal het let contact of contacts nemen en de of hoofdletter geven, en een aangepaste sleutel maken om ngForOf aan te maken.

In ons geval bouwt Angular een view die alles maakt vanaf de <li> tag naar binnen toe:

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

Het creëert ook een onzichtbare view-container die alle instanties van de sjabloon bevat en fungeert als een plaatshouder voor inhoud. De view-container die Angular heeft gemaakt, omsluit in wezen de “views”, in ons geval is dit net binnen de <ul> tags. Dit bevat alle templates die zijn gemaakt door ngFor (een voor elke rij).

Een pseudo-output zou er als volgt uit kunnen zien:

ngFor creëert een “embedded view” voor elke rij, waarbij de view die het heeft gemaakt en de context van de rij (de index en de rij-gegevens) worden doorgegeven. Deze ingebedde view wordt dan ingevoegd in de view container. Wanneer de gegevens veranderen, volgt het de items om te zien of ze zijn verplaatst. Als ze zijn verplaatst, in plaats van de ingesloten weergaven opnieuw te maken, worden ze verplaatst om op de juiste positie te komen, of ze worden vernietigd als ze niet langer bestaan.

Context en het doorgeven van variabelen

De volgende stap is begrijpen hoe Angular de context doorgeeft aan elke <contact-card>:

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

Dus nu hebben we ngFor en ngForOf begrepen, hoe associeert Angular let-contact met de individuele contact waar we dan property bind aan?

Omdat let-contact geen waarde heeft, het is slechts een attribuut, biedt Angular hier een “impliciete” waarde, of $implicit zoals het onder de motorkap wordt genoemd.

Terwijl Angular elk ngFor-item maakt, gebruikt het een NgForOfContext-klasse naast een EmbeddedViewRef, en geeft het deze eigenschappen dynamisch door. Hier is een klein fragment uit de broncode:

Naast deze sectie code kunnen we ook zien hoe onze eerder genoemde index en count eigenschappen worden bijgehouden:

U kunt de broncode van de richtlijn hier in meer detail doornemen.

Dit is hoe we dan de index en count zo kunnen benaderen:

Merk op hoe we let-i en let-c waarden aanleveren die worden blootgesteld vanuit de NgForRow instantie, in tegenstelling tot let-contact.

Om meer technieken, best practices en kennis van experts uit de praktijk te leren, raad ik je aan om mijn Angular cursussen te volgen – ze zullen je begeleiden op je reis om Angular volledig te beheersen!

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *