Angular 2 proponuje nam całkowicie nowe podejście dotyczące serwisów w aplikacji. W Angularze 1.x byliśmy mocno ograniczeni, wszystkie serwisy były singletonami oraz aplikacja posiadała wyłącznie jeden injector. Nowy Angular porzuca te ograniczenia oferując nam Injector tree.
INJECTOR TREE I INSTANCJE SERWISÓW
Injectory są ściśle powiązane z pojęciem dependency injection, który w ogólnym pojęciu jest wzorcem projektowym. W kontekście angulara, DI (dependency injection) jest całym systemem, bez którego wykonanie aplikacji jest praktycznie niemożliwe. Natomiast Injector w Angularze, jest obiektem, który dostarcza metody do tworzenia instancji zależności.
Angular 2 ma przynajmniej jeden Injector (Root Injector), na głównym poziomie aplikacji. Nie musimy go tworzyć, powstaje samoistnie w tle podczas załadowania aplikacji, w pliku main.ts:
1 |
platformBrowserDynamic().bootstrapModule(AppModule); |
Pozostaje nam go tylko skonfigurować poprzez providers, które są odpowiedzialne za tworzenie serwisów. Provider mówi Injectorowi, w jaki sposób ma stworzyć instancję zależności, wydaje instrukcję.
Czym się różni dodanie serwisu w NgModule a w komponencie?
Wszystkie serwisy, które dodamy w głównym ngModule do tablicy providers: […], zostają zarejestrowane przez Root Injector. Stają się singletonami i są dostępne dla każdego komponentu w aplikacji, wystarczy wstrzyknąć dany serwis do konstruktora w klasie komponentu.
W przypadku dodawania serwisu do providers bezpośrednio w komponencie, powstaje nowy Injector i tworzy nową instancję serwisu, która staje się singletonem dla tego komponentu i wszystkich jego dzieci.
Zobrazujmy sobie to na podstawie drzewa, jakiejś przykładowej, wymyślonej struktury:
Jak widzimy, Injector Tree biegnie równolegle do Component Tree. W komponencie OrderProductComponent, dodaliśmy jeszcze raz ProductService do providers. W tym momencie, powstał nowy Injector wraz z nową instancją serwisu. Komponent OrderProduct oraz jego dzieci, będą korzystać już z tej instancji serwisu, a nie z głównej instancji pochodzącej z Root Injector. Brzmi sensownie.
ZŁAMANIE DOMYŚLNEGO SCHEMATU
W naszym diagramie widzimy, że komponenty najniższej położone w drzewie, OrderProductDetail oraz PriceProductDetail korzystają z tego samego ProductService. W przypadku usunięcia ProductService z providers w OrderProductComponent, wszystkie komponenty z danego pod-drzewa, zaczną szukać wstrzykniętego serwisu u Injectora wyżej, aż po sam root injector, póki nie zostaną zaspokojone poprzez kolejne napotkane Providers. Jeśli ostatecznie nie trafią na poszukiwany serwis w żadnej tablicy Providers po drodze ani w Root Injector, angular wypluje nam błąd.
Wyobraźmy sobie teraz scenariusze:
- OrderProductComponent w przypadku nieznalezienia serwisu ProductService w tablicy providers ma zaprzestać poszukiwania (domyślnie sięgnie po instancję serwisu z Root Injectora)
- OrderProductComponent ma korzystać z instancji serwisu ProductService pochodzącej z Root Injectora, natomiast jego dzieci-komponenty, mają korzystać już z nowej instancji serwisu, pochodzącej z Order Product Injector
Angular umożliwia nam wyłamanie się z domyślnego schematu, za pomocą dekoratorów @SkipSelf() oraz @Host().
W pierwszym przypadku, jeśli chcemy zablokować szukanie serwisu w parent Injectorach , zrobimy to w następujący sposób:
1 2 3 4 5 6 7 8 |
@Component({ providers: [ProductService] }) export class OrderProductComponent { constructor(@Host() productService: ProductService) { } } |
Dodanie dekoratora @Host(), zapewni nam, że OrderProductComponent sięgnie tylko po ProductService z providers dodanym na poziomie tego komponentu. Jeśli go tam nie znajdzie – nie sięgnie po instancję ProductService z Root Injectora oraz zwróci nam błąd w konsoli, że występuje brak providera dla danego serwisu.
W drugim scenariuszu, jeśli chcemy aby OrderProductComponent skorzystał z instancji ProductService pochodzącej z Root Injectora, musimy użyć dekoratora @SkipSelf():
1 2 3 4 5 6 7 8 |
@Component({ providers: [ProductService] }) export class OrderProductComponent { constructor(@SkipSelf() productService: ProductService) { } } |
Należy pamiętać, że użycie @SkipSelf() dotyczy tylko komponentu, które ma zadeklarowane providers: […]. Wszystkie dzieci tego komponentu, skorzystają już z nowej instancji serwisu. Nie zapomnij również zaimportować SkipSelf oraz Host z @angular/core.
Podsumowanie
Podsumowując, Angular 2 wprowadził rewolucję w Dependency Injection w stosunku do Angulara 1.x. Sposób operowania DI jest przejrzysty, a dzięki narzędziom typu Augury, możemy w graficzny sposób sprawdzić jak wygląda nasze drzewo Injectorów.
Dzieki za ciekawy artykul, terez bede tu zagladac czesto
Pingback: ANGULAR 2 Augury - debugging aplikacji - Angular.love