27 paź 2017
5 min

ANGULAR – NgForTemplate & NgForOfContext

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:

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:

@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ę:

 <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:

<ng-template let-car>
  <p style="background: red; color: white;"> {{ car.name }} </p>
</ng-template>

Zanim przejdziemy dalej, zwróć uwagę na:

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ć:

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.

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]:

 <ng-template let-car ngFor [ngForOf]="cars" [ngForTemplate]="carTemplate"></ng-template>

NgForOfContext

W poprzednim akapicie, widzimy coś takiego jak NgForOfContext:

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:

 <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):

LIVE EXAMPLE

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:

<car-item [car]="car" *ngFor="let car of cars"></car-item>
<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ę.

Podziel się artykułem

Zapisz się na nasz newsletter

Dołącz do community Angular.love i bądź na bieżąco z trendami.