Angular NgFor, – kompletny przewodnik

W tym poście dowiesz się, jak używać dyrektywy Angulara NgFor do zapętlania danych w celu renderowania danych lub komponentów. Renderowanie listy <todo-item> komponentów byłoby świetnym przypadkiem użycia NgFor.

Jako że Angular jest frameworkiem reaktywnym, często można zobaczyć NgFor używane razem z obserwowalnymi, więc nasze przykłady kodu również będą w stylu reaktywnym. NgFor obsługuje również tablice i obiekty tablicopodobne – zbadamy każde z tych podejść.

Czym jest NgFor?

NgFor jest jedną z najczęściej używanych dyrektyw Angulara, która jest dostarczana wraz z Angular’s CommonModule.

🙌 Wskazówka: Dołącz BrowserModule do modułu głównego swojej aplikacji, ponieważ zawiera on już CommonModule dla nas!

NgFor pozwala nam zapętlić dane i uzyskać dostęp do każdego value i index – podobnie jak w przypadku zwykłego Array ForEach.

Dyrektywa NgFor robi również o wiele więcej niż tylko pętla i daje nam wartość i indeks, może być połączona z obserwowalnymi przez async pipe lub zwiększyć naszą wydajność renderowania z trackBy funkcją, którą możemy dostarczyć.

Dla tego artykułu będziemy włączać kolejny komponent ContactCardComponent do naszego @NgModule:

Nasz ContactCardComponent przyjmuje pojedynczy @Input z contact:

Więc teraz mamy już wszystko skonfigurowane, co dalej?

Iterowanie kolekcjami

Teraz, gdy nasz ContactCardComponent jest zawarty w naszym module, możemy skonfigurować nasz AppComponent tak, aby korzystał z tego zbioru danych:

Jak wspomniano we wstępie, używam Observable.of tutaj z RxJS, aby dać mi strumień Observable z wyników, jest to miły sposób naśladowania odpowiedzi Observable, takich jak podczas używania modułu Angulara HttpClient do zwracania danych z API.

Dlaczego w praktyce

Jak już jesteśmy skonfigurowani, możemy zajrzeć do naszego szablonu AppComponent:

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

Widzisz, że deklaruję <contact-card> wewnątrz tutaj, ponieważ chcemy iterować po naszym zbiorze danych i wypełniać każdy kontakt poprzez @Input ustawiony wewnątrz naszego ContactCardComponent.

Jednym ze sposobów, w jaki moglibyśmy to zrobić, jest użycie ngFor w samym komponencie, jednak dla uproszczenia użyjemy listy nieuporządkowanej. Dodajmy więc ngFor:

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

Działa się tutaj kilka rzeczy, pierwszą z nich jest zauważenie znaku * na początku ngFor, Do tego, co to oznacza, przejdziemy w następnej części, kiedy przyjrzymy się elementowi <ng-template>. Po drugie, tworzymy kontekst o nazwie contact, używając pętli „for of”.

Dyrektywa ngFor sklonuje element <li> oraz jego węzły podrzędne. W tym przypadku <contact-card> jest węzłem potomnym, a karta zostanie „wytłoczona” w DOM dla każdego konkretnego elementu wewnątrz naszej kolekcji contacts.

🎉 Pobierz za darmo!

Wyjdź poza Array ForEach. Nabierz pewności siebie z bardziej zaawansowanymi metodami, takimi jak Reduce, Find, Filter, Every, Some i Map i w pełni zrozum, jak zarządzać strukturami danych JavaScript.

  • W pełni zrozumieć, jak zarządzać strukturami danych JavaScript z niezmiennymi operacjami
  • 31 stron składni deep-dive, rzeczywistych przykładów, wskazówek i sztuczek
  • Napisz czystszą i lepiej ustrukturyzowaną logikę programowania w ciągu 3 godzin

✅ Sukces! Sprawdź swój e-mail, ciesz się.

Jako dodatkowy bonus, wyślemy Ci również kilka dodatkowych smakołyków przez kilka dodatkowych e-maili.

Więc teraz mamy contact dostępny jako indywidualny Obiekt, możemy przekazać indywidualny contact do „:

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

Jeśli używamy statycznej tablicy, lub wiążemy wynik z Observable do szablonu, możemy pozostawić szablon w obecnej postaci. Opcjonalnie możemy jednak powiązać obserwowalne bezpośrednio z szablonem, co oznacza, że będziemy potrzebowali rury async, aby wszystko zakończyć:

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

Używanie trackBy dla kluczy

Jeśli pochodzisz z tła AngularJS, prawdopodobnie widziałeś „track by” podczas używania ng-repeat, i podobnie w React land, używając key na elemencie kolekcji.

Co więc one robią? Asocjują obiekty, lub klucze, z konkretnymi węzłami DOM, więc jeśli coś się zmieni lub będzie musiało zostać ponownie wyświetlone, framework może to zrobić znacznie sprawniej. Angular’s ngFor domyślnie używa sprawdzania tożsamości obiektów dla ciebie, co jest szybkie, ale może być szybsze!

To właśnie tutaj trackBy wchodzi w grę, dodajmy trochę więcej kodu, a następnie wyjaśnijmy:

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

W tym miejscu dodaliśmy trackBy, następnie nadaliśmy mu wartość trackById. To jest funkcja, którą dodamy w klasie komponentu:

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

Wszystko, co robi ta funkcja, to użycie niestandardowego rozwiązania śledzenia dla naszej kolekcji. Zamiast używać tożsamości obiektu, mówimy Angularowi tutaj, aby używał unikalnej id właściwości, którą zawiera każdy contact obiekt. Opcjonalnie możemy użyć właściwości index (która jest indeksem w kolekcji każdego elementu, tj. 0, 1, 2, 3, 4).

Jeśli twój interfejs API zwraca unikalne dane, to użycie tego byłoby lepszym rozwiązaniem niż index – ponieważ index może ulec zmianie, jeśli zmienisz kolejność swojej kolekcji. Użycie unikalnego identyfikatora pozwala Angularowi znacznie szybciej zlokalizować węzeł DOM powiązany z obiektem, a także ponownie wykorzystać komponent w DOM, jeśli zajdzie potrzeba jego aktualizacji – zamiast niszczyć go i odbudowywać.

Przechwytywanie „index” i „count”

Dyrektywa ngFor nie kończy się tylko na iteracji, zapewnia nam również kilka innych przyjemności. Zbadajmy index i count, dwie publiczne właściwości wystawione nam na każdą ngFor iterację.

Utwórzmy kolejną zmienną o nazwie i, do której przypiszemy wartość index. Angular eksponuje dla nas te wartości pod maską, a gdy spojrzymy na kolejną sekcję z elementem <ng-template>, możemy zobaczyć, jak są one skomponowane.

Aby wylogować indeks, możemy po prostu interpolować i:

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

Da nam to każdy indeks, począwszy od 0, dla każdego elementu w naszej kolekcji. Wyeksponujmy również count:

count zwróci nam długość kolekcji na żywo, odpowiednik contacts.length. Te mogą być opcjonalnie związane i przekazywane do każdego komponentu, na przykład możesz chcieć wylogować całkowitą długość kolekcji gdzieś, a także przekazać index konkretnego kontaktu do funkcji @Output:

Dostęp do pierwszego, ostatniego, nieparzystego, parzystego

Cztery kolejne właściwości eksponowane przez ngFor (no, właściwie pod spodem korzysta z NgForOfContext, klasy, która wewnętrznie generuje każdy kontekst ngFor). Przyjrzyjmy się szybko kodowi źródłowemu do tego celu:

Jak wspomniałem powyżej, NgForOfContext jest tym, co konstruuje nasze ngFor elementy, i możesz zobaczyć, że w constructor przyjrzeliśmy się już index i count! Ostatnie rzeczy, którym musimy się przyjrzeć, to gettery, które możemy wyjaśnić na podstawie powyższego kodu źródłowego:

  • first: zwraca true dla pierwszego elementu w kolekcji, dopasowuje indeks do zera
  • last: zwraca true dla ostatniego elementu w kolekcji, pasuje do indeksu z całkowitą liczbą, minus jeden, aby przesunąć „licznik” w dół o jeden, aby zaspokoić indeksy oparte na zerze
  • even: zwraca true dla parzystych elementów (np.np. 2, 4) w kolekcji, używa % operatora modulus do obliczenia na podstawie indeksu
  • odd: zwraca true dla nieparzystych elementów (np. 1, 3), po prostu odwraca this.even wynik

Używając tego, możemy dodać warunkowe zastosowanie takich rzeczy jak stylizacja, lub podpiąć się do właściwości last aby wiedzieć kiedy kolekcja zakończyła renderowanie.

Dla tego szybkiego demo, użyjemy ngClass aby dodać kilka stylów do każdego <li> (zauważ jak tworzymy więcej zmiennych, tak jak index):

I kilka stylów:

Nie będziemy demonstrować first i last, ponieważ jest to dość oczywiste z powyższego, jak możemy je podpiąć!

element

Wcześniej w tym artykule wspomnieliśmy, że przyjrzymy się zrozumieniu co oznacza * w naszych szablonach. Składnia tego elementu jest taka sama jak *ngIf, który prawdopodobnie też już widziałeś.

Więc w następnej sekcji, weźmiemy głębiej pod lupę ngFor* oraz element <ng-template>, aby wyjaśnić bardziej szczegółowo, co tak naprawdę się tutaj dzieje.

Kiedy używamy gwiazdki (*) w naszych szablonach, informujemy Angulara, że używamy dyrektywy strukturalnej, która jest również składnią cukru (miłym skrótem) dla użycia elementu <ng-template>.

i Web Components

Czym więc jest element <ng-template>? Po pierwsze, zróbmy krok wstecz. Cofniemy się, aby pokazać tutaj trochę kodu AngularJS, być może robiłeś to już wcześniej lub zrobiłeś coś podobnego w innym frameworku/bibliotece:

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

To nadpisuje type na tagu <script>, który uniemożliwia silnikowi JavaScript parsowanie zawartości tagu <script>. Dzięki temu my, lub framework taki jak AngularJS, możemy pobrać zawartość znacznika script i użyć go jako pewnego rodzaju szablonu HTML.

Web Components wprowadziło kilka lat temu nową specyfikację podobną do tego pomysłu, zwaną <template>:

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

Aby złapać nasz powyższy szablon i go zainicjować, zrobilibyśmy to w zwykłym JavaScripcie:

Zauważ jak mamy id=host, który jest naszym „hostem” Node’a, do którego szablon ma zostać wstrzyknięty.

Mogłeś zobaczyć ten termin w Angular na kilka sposobów, takich jak _nghost prefiksy na Nodes (ng-host) lub host własność w dyrektywach.

ngFor i ng-template

Przede wszystkim, <ng-template> jest własną implementacją Angulara znacznika <template>, pozwalającą nam myśleć o projektowaniu aplikacji w komponentach webowych i ideach za nimi stojących. Zapewnia nam również więcej mocy niż element <template> daje nam domyślnie, płynnie wpasowując się w sposób, w jaki Angular kompiluje nasz kod.

Jak więc powyższe wyjaśnienie <template> mówi nam więcej o ngFor i *? Gwiazdka jest skrótem składniowym do użycia elementu <ng-template>.

Zacznijmy od podstawowego przykładu ngFor:

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

I zademonstrujmy odpowiednik <ng-template>:

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

To jest zupełnie co innego! Co się tutaj dzieje?

Gdy używamy *ngFor, mówimy Angularowi, aby traktował element, z którym związany jest * jako szablon.

Angularowy element <ng-template> nie jest prawdziwym Web Componentem (w przeciwieństwie do <template>). Odzwierciedla on jedynie koncepcje stojące za nim, aby umożliwić użycie <ng-template> tak, jak jest to zamierzone w specyfikacji. Kiedy skompilujemy nasz kod (JiT lub AoT), nie zobaczymy żadnych elementów <ng-template> wyprowadzonych w DOM. Nie oznacza to jednak, że nie możemy używać takich rzeczy jak Shadow DOM, ponieważ są one nadal całkowicie możliwe.

Kontynuujmy i zrozummy, co ngForlet-contact i ngForOf robią powyżej.

ngFor i osadzone szablony widoków

Pierwsza rzecz, ngFor jest dyrektywą! Sprawdźmy część kodu źródłowego:

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

Tutaj Angular używa selektorów atrybutów jako wartości selector, aby powiedzieć dekoratorowi @Directive jakich atrybutów szukać.

Dyrektywa używa , co sugeruje, że istnieją dwa atrybuty jako selektor łańcuchowy. Jak więc działa ngFor, jeśli nie używamy ngForOf?

Kompilator Angulara przekształca wszelkie elementy <ng-template> i dyrektywy użyte z gwiazdką (*) w widoki, które są oddzielne od widoku komponentu głównego. To dlatego każdy widok może być tworzony wiele razy.

Podczas fazy kompilacji weźmie let contact of contacts i skapitalizuje of, i utworzy niestandardowy klucz, aby utworzyć ngForOf.

W naszym przypadku Angular skonstruuje widok, który utworzy wszystko od znacznika <li> do wewnątrz:

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

Tworzy również niewidzialny kontener widoku, który ma zawierać wszystkie instancje szablonu, działając jako placeholder dla treści. Kontener widoku, który Angular stworzył, zasadniczo owija „widoki”, w naszym przypadku jest to tuż wewnątrz tagów <ul>. To mieści wszystkie szablony, które są tworzone przez ngFor (jeden dla każdego wiersza).

Pseudo wyjście może wyglądać tak:

ngFor tworzy „widok osadzony” dla każdego wiersza, przechodząc przez widok, który utworzył i kontekst wiersza (indeks i dane wiersza). Ten osadzony widok jest następnie wstawiany do kontenera widoku. Gdy dane się zmieniają, śledzi elementy, aby sprawdzić, czy zostały przeniesione. Jeśli się przesunęły, zamiast odtwarzać widoki osadzone, przesuwa je, aby znajdowały się we właściwej pozycji lub niszczy je, jeśli już nie istnieją.

Kontekst i przekazywanie zmiennych

Kolejnym krokiem jest zrozumienie, w jaki sposób Angular przekazuje kontekst do każdego <contact-card>:

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

Więc teraz zrozumieliśmy ngFor i ngForOf, w jaki sposób Angular kojarzy let-contact z poszczególnymi contact, do których następnie wiążemy właściwości?

Ponieważ let-contact nie ma wartości, jest tylko atrybutem, to właśnie tutaj Angular zapewnia „implikowaną” wartość, lub $implicit jak to się nazywa pod maską.

Podczas gdy Angular tworzy każdy element ngFor, używa klasy NgForOfContext obok EmbeddedViewRef, i przekazuje te właściwości dynamicznie. Oto mały wycinek z kodu źródłowego:

Poza tym fragmentem kodu, możemy również zobaczyć jak nasze wspomniane wcześniej index i count właściwości są aktualizowane:

Możesz przekopać się przez kod źródłowy dyrektywy bardziej szczegółowo tutaj.

W ten sposób możemy następnie uzyskać dostęp do index i count w ten sposób:

Zauważ, jak dostarczamy let-i i let-c wartości, które są eksponowane z instancji NgForRow, w przeciwieństwie do let-contact.

Aby poznać więcej technik, najlepszych praktyk i wiedzy ekspertów w świecie rzeczywistym, gorąco polecam sprawdzenie moich kursów Angulara – poprowadzą Cię one przez Twoją podróż do opanowania Angulara w pełni!

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *