Transclusion, Injection and Procrastination

Néhány hónappal ezelőtt Kapunahele Wong és én ötleteket gyűjtöttünk, hogy milyen előadásokat tarthatnánk együtt. Ez körülbelül akkor volt, amikor úgy döntöttem, hogy felfedezem az Ivy-t, így úgy gondoltam, hogy ez jó lenne egy beszélgetésnek. Kapunahele-nek azonban más ötlete volt:

Twitter, ahol a vicces dolgok történnek!

Először hallottam az Injector Treesről, ezért kíváncsi voltam. Kapunahele megosztotta velem néhány előzetes kutatását, és megtudtam, hogy az Angular Injector függőségi feloldó algoritmusa nem is olyan egyszerű, mint azt korábban gondoltam.

Sőt, mivel az NgModules-t az Angular 2.0 5. kiadásjelöltjében vezették be.0, csak modulszinten nyújtottam szolgáltatásokat, a jól ismert providers tulajdonság használatával:

Vagy az Angular 6 óta az Injectable dekorátor providedIn tulajdonságát használva:

Mindenesetre mindig modulszinten deklaráltam a szolgáltatásaimat, és nem fordítottam túl nagy figyelmet a többi lehetőségre.

Enter Injector Trees 🌴

Kapunahele és én úgy döntöttünk, hogy benyújtjuk az Injector Trees előadásunkat az Angular Connectre. Néhány hónappal később a következő üzenet landolt a postaládámban:

Lelkesek voltunk, az előadásunkat elfogadták a konferenciára! 😊

A következő heteket azzal töltöttük, hogy felfedezzük annak minden rejtett zugát, hogyan működik a függőségi injektálás az Angularban. Ebben a blogbejegyzésben ezek közül fogok megosztani veletek néhányat.

Starting Simple

Egy egyszerű Angular alkalmazással fogunk kezdeni. Ez az alkalmazás rendelkezik egy “Kingdom” szolgáltatással és egyetlen komponenssel, amely befecskendezi a Kingdomot és megjeleníti a Kingdom nevét:

A példához a Dragon mellett döntöttem, mivel szeretem őket használni pontosvessző helyett a kódomban.

Azért, hogy a dolgok egy kicsit érdekesebbek legyenek, fűszerezzük az alkalmazásunkat egy Unicorn komponenssel. Ez a komponens kiírja annak a királyságnak a nevét, ahol él:

Szóval van egy alkalmazáskomponensünk, és benne egy unikornis. Nagyszerű!

Most mi történik, ha megváltoztatjuk a AppComponent definícióját, hogy más értéket adjunk a KingdomService-nak?

Ezt úgy tehetjük meg, hogy a következő sort hozzáadjuk a komponens deklarációjához:

providers: 

Milyen hatással lesz ez az alkalmazásunkra? Próbáljuk ki és nézzük meg:

Mint láthatjuk, a KingdomService-nak a AppComponent-ban definiált érték elsőbbséget kapott az AppModule-ban definiált szolgáltatással szemben (nem közvetlenül ott definiáltuk, hanem a providedIn segítségével, de az eredmény ugyanaz).

Elemfa, modulfa

Az ok, amiért zombikat látunk, az az Angular függőségi felbontásának módja. Először az elemek fáját keresi, és csak utána a modulok fáját. Nézzük csak UnicornComponent. A KingdomService egy példányát injektálja a konstruktorán belül:

constructor(public kingdom: KingdomService) {}

Amikor az Angular létrehozza ezt a komponenst, először megnézi, hogy vannak-e a komponenssel azonos elemen definiált szolgáltatók. Ezeket a szolgáltatókat regisztrálhattuk magában a komponensben, vagy egy direktíva segítségével. Ebben az esetben nem adtunk meg semmilyen értéket a KingdomService számára a UnicornComponent elemen belül, és a <app-unicorn> elemen sincs semmilyen direktíva.

A keresés ezután az elemfán felfelé folytatódik, a AppComponent elemig. Itt az Angular azt találja, hogy van egy értékünk a KingdomService számára, így beilleszti ezt az értéket, és itt megállítja a keresést. Ebben az esetben tehát az Angular meg sem nézte a modulok fáját.

Angular lusta!

Mint ahogy mi programozók, úgy az Angular is halogató. Nem hoz létre példányokat a szolgáltatásokból, hacsak nem muszáj. Erről meggyőződhetsz, ha a KingdomService konstruktorához hozzáadsz egy console.log utasítást (ha ma nosztalgiázni támad kedved, egy alert('I love marquees')-et is hozzáadhatsz).

Azt fogod látni, hogy a console.log utasítás soha nem kerül végrehajtásra – mivel az Angular nem hozza létre a szolgáltatást. Ha eltávolítod a providers: deklarációt a AppComponent-ból (vagy áthelyezed a UnicornComponent-be, hogy csak az unikornisra és annak gyermek elemeire vonatkozzon), akkor el kell kezdened látni a naplóüzenetet a konzolodban.

Most az Angularnak nincs választása – nem találja a KingdomService-t, amikor az Element Tree-ben keresgél. Így először a Module Injector Tree-re megy, majd látja, hogy ott adtuk meg a szolgáltatást, és végül létrehoz egy példányt belőle. Ezért a konstruktorban lévő kód lefut, és láthatja az ott elhelyezett debug-nyomtatást.

Direktívák inváziója!

Elmondtam, hogy a direktívák is adhatnak értékeket a függőségi injektáláshoz. Kísérletezzünk ezzel. Definiálunk egy új appInvader direktívát, amely a királyság értékét 👾-re változtatja.
Miért? Mert olyan szépek voltak a VR + Angular előadáson, amit Alex Castillo és én tartottunk az ng-conf-on.

Ezután adjunk hozzá egy másik <app-unicorn> elemet, és alkalmazzuk rá az új appInvader direktívát:

Amint várható volt, az új egyszarvú a 👾 királyságában él. Ennek oka, hogy az irányelv megadta a KingdomService értékét. És ahogy fentebb már elmagyaráztuk, az Angular az aktuális elemtől kezdi a keresést, megnézi a komponenst és az összes direktívát, és csak ha ott nem találja a kért értéket, akkor megy tovább felfelé az elemfán (majd a modulokon).

Nézzünk egy kicsit bonyolultabb dolgot:

Tartalom-projektáló erdő hozzáadása az alkalmazáshoz!

Egy új Forest komponenst fogunk hozzáadni az alkalmazásunkhoz, és az egyszarvúak egy részét ebbe az erdőbe helyezzük, mert ott élnek az egyszarvúak (ezt valami random fickó mondta a quora-n, tehát biztos igaz).

A Forest komponens egyszerűen egy konténer, amely Content Projectiont használ, hogy gyermekeit egy zöld “erdei” háttér tetején jelenítse meg:

Így látjuk a AppForest komponens elemeit egy füves háttéren, majd az összes kivetített tartalmat egy élénkzöld háttér tetején. És mivel az alkalmazáskomponensünkön belül megadtuk a KingdomService értékét, minden, ami benne van, örökli azt (kivéve az egyetlen egyszarvút a appInvader direktívával).

De mi történik, ha új értéket adunk a KingdomService-nak a ForestComponent-n belül? A kivetített tartalom (amit a AppComponent sablonban definiáltunk) is ezt az új értéket kapja a királysághoz? Vagy továbbra is a 🧟 királyságban lesz? Ki tudod találni?

Régebben ezt Transzklúziónak hívták. Most “Tartalmi kivetítésnek” hívják. Fotó: ng-conf

Az erdő varázslója

Egyetlen sorral egészítjük ki az előző példánkat, megadva egy 🧙 királyságot a ForestComponent számára:

providers: 

És ez az eredmény:

Most ez érdekes – a királyságok keveredését látjuk az erdőn belül! Maga az erdő elem a 🧙 királyságban él, de a vetített tartalom mintha megosztott személyiség lenne: az egyszarvúak is a 🧙 királysághoz tartoznak, de a felettük lévő szöveg a 🧟 királyságot mutatja…

Az egyszarvúakat és a szöveget is ugyanazon a helyen, a app.component.html sablon 12-15. sorában definiáltuk. Ami azonban számít, az az a hely, ahol magát a komponenst létrehoztuk a DOM-ban. A 12. sorban lévő szöveg valójában ugyanaz, mint amit a 4. sorban csinálunk – ugyanannak a AppComponent példánynak a kingdom tulajdonságát olvassuk ki. Ennek a komponensnek a DOM eleme valójában a <app-forest> DOM elem egyik őse. Tehát amikor ezt a AppComponent példányt létrehoztuk, be lett injektálva a 🧟 királysággal.

A két <app-unicorn> elem azonban a <app-forest> DOM elemeken belül van, így amikor a UnicornComponents példányaik létrejönnek, az angular valójában felmegy a DOM-ra és látja az értéket, amit a KingdomService-nak adtunk meg a ForestComponent-en belül, és így ezek az egyszarvúak a 🧙 királysággal vannak injektálva.

Más viselkedést érhetsz el, ha a ForestComponent definiálásakor a providers-et viewProviders-re cseréled. A View Providers-ről többet megtudhatsz itt, és megnézheted ezt a kódpéldát is, ahol megváltoztattam a ForestComponent-t a View Providers használatára, így most már az erdőben lévő egyszarvúak is be vannak injektálva a 🧟 királysággal. Köszönöm Lars Gyrup Brink Nielsennek, hogy rámutatott erre!

A Chrome T-Rexet ebben a blogbejegyzésben

Keep Exploring!

Remélem, hogy most tanultál valami újat az Angular Dependency Injection rendszeréről. Ez csak egy azok közül a dolgok közül, amelyeket Kapunahele és én feltártunk, amikor az AngularConnectre készítettük előadásunkat. Van még sok minden más is – meghívunk titeket, hogy fedezzétek fel tovább, és az előadás után meg fogjuk osztani a diákat és a videó linkjét is. Ja, és lesz némi élő kódolás is. Nagyon jó móka lesz!

Ha szeretnél többet megtudni az Angular Injector minden csínjáról-bínjáról, itt van néhány cikk, amit nagyon hasznosnak találtam:

  • Amit mindig is tudni akartál az Angular Dependency Injectionról
  • A curious case of the @Host decorator and Element Injectors in Angular
  • Hierarchical Dependency Injectors (Angular Docs)

És ha részt veszel az AngularConnect-en, akkor gyere és köszönj!

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.