Emocje po konkursie opadły, więc czas dalej dowozić nowy kontent na bloga! Od jakiegoś czasu trapiła mnie (ahh, te problemy developera) nieznajomość tworzenia list z dowolną templatką, umieszczoną w <ng-template>.
NgForTemplate
Jeśli spojrzymy do dokumentacji dyrektywy ngFor, to możemy zuważyć settera:
1 |
set ngForTemplate: TemplateRef<NgForOfContext<T>> |
…który jako typ, przyjmuje TemplateRef, który reprezentuje np. jakiś wycinek kodu HTML osadzony w tagach <ng-template>.
Dzięki ngForTemplate, możemy w naszym selektorze komponentu z listą, umieścić dowolny html i użyć go jako kontekstu w NgFor. Przełóżmy to na kod, gdzie wykorzystamy w/w ngForTemplate. Stworzymy listę samochodów:
1 2 3 4 5 6 7 8 9 10 11 |
@Component({ selector: 'cars-list', template: ` <ng-template let-car ngFor [ngForOf]="cars" [ngForTemplate]="carTemplate"></ng-template> `, }) export class CarsListComponent { @Input() cars: Car[]; } |
W powyższym kodzie, carTemplate wskazuje na templatkę przekazaną do naszego komponentu. Więc sięgamy po nasz komponent <cars-list> w widoku gdzie chcemy go wykorzystać i przekazujemy do niego sobie dowolną templatkę:
1 2 3 4 5 |
<cars-list [cars]="cars"> <ng-template let-car> <p style="background: red; color: white;"> {{ car.name }} </p> </ng-template> </cars-list> |
Czyli nasz carTemplate, przekazany do [ngForTemplate], jest dokładnie tym kawałkiem kodu HTML:
1 2 3 |
<ng-template let-car> <p style="background: red; color: white;"> {{ car.name }} </p> </ng-template> |
Zanim przejdziemy dalej, zwróć uwagę na:
1 |
let-car |
Let- wskazuje na element, po którym idą kolejne iteracje – w naszym przypadku jest to obiekt car, z którego odczytujemy np. propercję car.name. Tych obiektów może być więcej, możesz napisać:
1 |
let-car let-driver let-city |
Let tworzy zatem lokalna zmienną dla templatki do której możemy się zbindować.
Jednak nadal, powyższy kawałek HTML z paragrafem i nazwą samochodu, nie zostanie wyrenderowany. Brakuje nam referencji do tej templatki z poziomu komponentu <cars-list>. Więc musimy ją stworzyć, za pomocą dekoratora @ContentChild.
1 2 3 |
export class CarsListComponent { @Input() cars: Car[]; @ContentChild(TemplateRef) carTemplate: TemplateRef<NgForOfContext<Car>>; |
Sięgamy po nasz zagnieżdzony kontent o typie TemplateRef za pomocą ContentChild, zapisujemy go pod nazwą carTemplate, a ten carTemplate ląduje sobie w naszym [ngForTemplate]:
1 |
<ng-template let-car ngFor [ngForOf]="cars" [ngForTemplate]="carTemplate"></ng-template> |
NgForOfContext
W poprzednim akapicie, widzimy coś takiego jak NgForOfContext:
1 |
TemplateRef<NgForOfContext<Car>>; |
TemplateRef musi być zinterpretowany w zadanym kontekście i właśnie tutaj wskazujemy jaki jest kontekst TemplateRefa w naszym ngFor.
Wykorzystajmy to co przygotowaliśmy, możemy już do naszego komponentu <cars-list>, przekazywać dowolne templatki:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<cars-list [cars]="cars"> <ng-template let-car> <p style="background: red; color: white;"> {{ car.name }} </p> </ng-template> </cars-list> <cars-list [cars]="cars"> <ng-template let-car> <p> {{ car.name }} / <span style="color: red;"> {{ car.ps }} </span></p> </ng-template> </cars-list> <cars-list [cars]="cars"> <ng-template let-car> <button> {{ car.name }} </button> <span style="color: green;"> {{ car.ps }} </span> </ng-template> </cars-list> |
Plunker z kodem (jeśli się nie kompiluje, włącz tryb incognito w chrome):
PODSUMOWANIE
Być może w tym momencie myślisz, a po co mi to? przecież mogę templatki wydzielić do osobnego komponentu, np. <car-item> i wywołać to w następujący sposób:
1 |
<car-item [car]="car" *ngFor="let car of cars"></car-item> |
1 |
<car-item-detailed [car]="car" *ngFor="let car of cars"></car-item-detailed> |
Niby tak. Ale jeśli często iterujemy po tej samej tablicy na widoku, np. liście produktów, gdzie wyświetlamy ją z różnymi detalami (np. samymi nazwami, raz z samymi zdjęciami etc), to nie ma sensu tworzyć 5 osobnych <products-list>, lub 5 osobnych komponentów <product-item> o różnych templatkach. Można stworzyć listę raz i podrzucać tylko jej nowe templatki właśnie za pomocą NgForTemplate, tam gdzie sięgamy po tą listę.
„odczytujemy np. propercję” litości! Prowadzenie bloga w języku polskim jednak zobowiązuje do unikania tak parszywego żargonu.
Masz rację, właściwość byłaby odpowiedniejszym słowem 🙂 tym niemniej moim zdaniem słowo propercja weszła już do żargonu 😉