Referencja lokalna to kolejny mechanizm w Angular 2 umożliwiający komunikację pomiędzy komponentami. Dzięki niej, z poziomu rodzica jesteśmy w stanie uzyskać dostęp do dowolnego elementu znajdującego się w szablonie komponentu – zarówno podstawowego tagu, jak i komponentu „dziecka”.
Po co i kiedy?
W poprzednim artykule omawiałem komunikację uzyskiwaną dzięki parametrom @Input
i @Output
. W tamtym przypadku, to komponent decydował o udostępnianiu odpowiedniego interfejsu służącego do komunikacji. To nie zawsze jest wystarczające rozwiązanie.
Jak to osiągnąć?
Czasami chcielibyśmy uzyskać dostęp do komponentu, który nie dał nam takiej możliwości lub do zwykłego tagu – np. input
w celu ręcznego wyłuskania wartości. Jednym z rozwiązań jest właśnie referencja lokalna. Utworzenie referencji lokalnej jest niezwykle proste. Wystarczy, że w szablonie komponentu oznaczymy interesujący nas element w taki sposób:
<add-todo (addTodo)="addTodo($event)" #addTodoRef></add-todo>
Oczywiście – tak jak wspomniałem wcześniej – nic nie stoi na przeszkodzie, aby uzyskać dostęp do dowolnego, podstawowego tagu:
<h2 #ref>Todo App</h2>
To wszystko. Teraz powstaje pytanie – w jaki sposób dostać się do tego z poziomu klasy komponentu?
@ViewChild
Aby w klasie dostać się do zadeklarowanej referencji, wykorzystujemy adnotację @ViewChild
w następujący sposób:
@ViewChild('ref') ref: ElementRef;
Jak możemy zauważyć, referencje wskazujące na podstawowe tagi mają typ ElementRef
. Z komponentami jest zupełnie inaczej:
@ViewChild('addTodoRef') addTodoRef: AddTodoComponent;
Typ jest równoważny klasie komponentu, dzięki czemu mamy swobodny dostęp do publicznych składowych (np. możemy z poziomu rodzica wywołać metodę dziecka). Pozostaje pytanie – czy nie da się tego zrobić w bardziej elegancki sposób?
Owszem, da się. Dekorator @ViewChild
ma jeszcze jedną, interesującą umiejętność. Jest w stanie wskazać komponent na podstawie typu, toteż powyższy zapis możemy przekształcić w następujący:
@ViewChild(AddTodoComponent) addTodoRef: AddTodoComponent;
Dzięki czemu nie musimy zaśmiecać kodu szablonu zbędnymi referencjami.
Uwaga! Dostęp do obiektów oznaczonych adnotacją
@ViewChild
mamy dopiero przy wystąpieniu zdarzenia cyklu życia komponentungAfterViewInit
.
Więcej dzieci tego samego typu
Wiemy już w jaki sposób dostać się do pojedynczego elementu. Wyobraźmy sobie, że nasz komponent zawiera przykładowo 10 komponentów dzieci tego samego typu (np. TodoItemComponent
). W jaki sposób dostać się do nich wszystkich? Przy pomocy adnotacji @ViewChildren
:
@ViewChildren(TodoItemComponent) todosRefsList: QueryList<TodoItemComponent>;
W rezultacie w zmiennej todosRefsList
odnajdziemy pełną kolekcję QueryList, udostępniającą garść pożytecznych metod, z którymi warto się zapoznać.
ContentChild(ren)
Zarówno @ViewChild
jak i @ViewChildren
nie mają dostępu do fragmentów transkludowanych szablonu.
W takim przypadku musimy skorzystać z odpowiedników – adnotacji @ContentChild
i @ContentChildren
. Sposób działania jest taki sam, z tym, że elementy dostępne są dopiero od momentu wystąpienia zdarzenia cyklu życia komponentu ngAfterContentInit
.
Podsumowanie
Dzisiaj omówiłem kolejny sposób umożliwiający komunikację pomiędzy komponentami. W następnym artykule zajmiemy się tematem serwisów, ponieważ są one ściśle związane z ostatnim z podstawowych typów komunikacji.