Exploring The Hidden Corners of Angular Dependency Injection
Muutama kuukausi sitten Kapunahele Wong ja minÀ mietimme ideoita puhe-ehdotuksista, jotka voisimme tehdÀ yhdessÀ. Se oli samoihin aikoihin, kun pÀÀtin tutustua Ivyyn, joten ajattelin, ettÀ se voisi sopia hyvin puheeksi. KapunahelellÀ oli kuitenkin erilainen ajatus:
Kuulin Injector TreesistÀ ensimmÀistÀ kertaa, joten olin kiinnostunut. Kapunahele jakoi kanssani osan alustavasta tutkimuksestaan, ja sain tietÀÀ, ettÀ Angular Injectorin riippuvuuksien ratkaisualgoritmi ei ollutkaan niin yksinkertainen kuin olin aiemmin luullut.
SitÀ lÀhtien, kun NgModules otettiin kÀyttöön Angular 2.0:n 5. julkaisukandidaatissa.0, tarjosin palveluita vain moduulitasolla kÀyttÀen tuttua providers-ominaisuutta:
Vai Angular 6:sta lÀhtien kÀyttÀen Injectable-dekoraattorin providedIn-ominaisuutta:
Joko niin tai nÀin, ilmoitin aina palveluni moduulitasolla, enkÀ koskaan kiinnittÀnyt kovinkaan paljon huomiota muihin mahdollisuuksiin.
Enter Injector Trees đŽ
Kapunahele ja minÀ pÀÀtimme lÀhettÀÀ Injector Trees -keskustelumme Angular Connectiin. Useita kuukausia myöhemmin seuraava viesti rantautui postilaatikkooni:
Olimme ihan riemuissamme, puheemme hyvĂ€ksyttiin konferenssiin! đ
Vietimme seuraavat viikot tutkimalla kaikkia piilossa olevia kulmia siitÀ, miten riippuvuusinjektio toimii Angularissa. TÀssÀ blogikirjoituksessa aion jakaa joitakin niistÀ kanssasi.
Aloitus yksinkertaisesti
Aloitamme yksinkertaisella Angular-sovelluksella. TĂ€ssĂ€ sovelluksessa on ”Kingdom”-palvelu ja yksi komponentti, joka injektoi Kingdomin ja nĂ€yttÀÀ Kingdomin nimen:
PÀÀdyin kÀyttÀmÀÀn esimerkissÀ lohikÀÀrmettÀ, koska rakastan kÀyttÀÀ niitÀ puolipisteiden sijasta koodissani.
TehdÀksemme asioista hieman mielenkiintoisempia, maustetaan sovelluksemme Unicorn-komponentilla. TÀmÀ komponentti tulostaa sen kuningaskunnan nimen, jossa se asuu:
MeillÀ on siis sovelluskomponentti ja sen sisÀllÀ yksisarvinen. Hienoa!
MitÀ tapahtuu nyt, kun muutamme AppComponent
:n mÀÀritelmÀÀ ja annamme KingdomService
:lle eri arvon?
Voimme tehdÀ tÀmÀn lisÀÀmÀllÀ komponentti-ilmoitukseen seuraavan rivin:
providers:
Miten tÀmÀ vaikuttaa sovellukseemme? Kokeillaan ja katsotaan:
Kuten nÀet, arvo, jonka mÀÀrittelimme KingdomService
:lle AppComponent
:ssa, sai etusijan AppModulissamme mÀÀriteltyyn palveluun nÀhden (sitÀ ei mÀÀritelty suoraan siellÀ, vaan kÀyttÀen providedIn
:aa, mutta lopputulos on sama).
Elementtipuu, modulipuu
Syy siihen, ettÀ nÀemme zombeja, on tapa, jolla riippuvuuksien resoluutio toimii Angularissa. Se etsii ensin komponenttien puun ja vasta sitten moduulien puun. Tarkastellaanpa UnicornComponent
. Se injektoi instanssin KingdomService
konstruktorinsa sisÀllÀ:
constructor(public kingdom: KingdomService) {}
Kun Angular luo tÀmÀn komponentin, se etsii ensin, onko samalle elementille kuin komponentti mÀÀritelty tarjoajia. NÀmÀ palveluntarjoajat on voitu rekisteröidÀ itse komponenttiin tai direktiivin avulla. TÀssÀ tapauksessa emme ole antaneet mitÀÀn arvoa KingdomService
:lle UnicornComponent
:n sisÀllÀ, eikÀ meillÀ ole mitÀÀn direktiivejÀ <app-unicorn>
-elementissÀ.
Haku jatkuu sitten elementtipuussa ylöspÀin, menemÀllÀ AppComponent
:een. TÀÀllÀ Angular havaitsee, ettÀ meillÀ on arvo KingdomService
:lle, joten se syöttÀÀ tÀmÀn arvon ja lopettaa haun siihen. TÀssÀ tapauksessa Angular ei siis edes katsonut moduulipuuhun.
Angular on laiska!
Aivan kuten me ohjelmoijat, myös Angular on viivyttelijÀ. Se ei luo palveluiden instansseja, ellei sen todella tarvitse. Voit varmistaa tÀmÀn lisÀÀmÀllÀ KingdomService
:n konstruktoriin console.log
-lausekkeen (voit myös lisÀtÀ alert('I love marquees')
, jos tunnet olosi nostalgiseksi tÀnÀÀn).
Voit nÀhdÀ, ettÀ console.log
-lauseketta ei koskaan suoriteta – koska Angular ei luo palvelua. Jos poistat providers:
-ilmoituksen AppComponent
:stÀ (tai siirrÀt sen UnicornComponent
:een, jolloin se koskee vain yksisarvista ja sen lapsielementtejÀ), sinun pitÀisi alkaa nÀhdÀ lokiviestiÀ konsolissasi.
Nyt Angularilla ei ole valinnanvaraa – se ei löydĂ€ KingdomService
:aa etsiessÀÀn Elementtipuusta. NiinpÀ se menee ensin Module Injector Tree -puuhun, nÀkee, ettÀ annoimme palvelun siellÀ, ja luo lopulta instanssin siitÀ. NÀin ollen konstruktorin sisÀllÀ oleva koodi toimii, ja nÀet sinne laittamasi debug-tulosteen.
Direktiivien hyökkÀys!
Mainitsin, ettÀ myös direktiivit voivat antaa arvoja riippuvuusinjektiota varten. Kokeillaanpa sitÀ. MÀÀrittelemme uuden appInvader
-direktiivin, joka muuttaa valtakunnan arvon đŸ:ksi.
Miksi? Koska ne olivat niin ihania VR + Angular -puheessa, jonka Alex Castillo ja minÀ pidimme ng-confissa.
Sitten lisÀÀmme toisen <app-unicorn>
-elementin ja sovellamme siihen uutta appInvader
-direktiiviÀ:
Odotetusti uusi yksisarvinen asuu đŸ:n kingdomissa. TĂ€mĂ€ johtuu siitĂ€, ettĂ€ direktiivi antoi arvon KingdomService
. Ja kuten edellÀ selitettiin, Angular aloittaa haun nykyisestÀ elementistÀ, tarkastelee komponenttia ja kaikkia direktiivejÀ, ja vasta jos se ei löydÀ pyydettyÀ arvoa sieltÀ, se jatkaa etenemistÀ elementtipuussa ylöspÀin (ja sitten moduuleissa).
Katsotaanpa jotain hieman monimutkaisempaa:
SisÀltöÀ projisoivan metsÀn lisÀÀminen sovellukseen!
LisÀÀmme sovellukseemme uuden Forest-komponentin ja laitamme osan yksisarvisista tÀmÀn metsÀn sisÀlle, koska siellÀ yksisarviset asuvat (joku satunnainen kaveri sanoi noin quorassa, joten sen tÀytyy olla totta).
MetsĂ€-komponentti on yksinkertaisesti sĂ€iliö, joka kĂ€yttÀÀ sisĂ€llön projisointia nĂ€yttÀÀkseen lapsensa vihreĂ€n ”metsĂ€isen” taustan pÀÀllĂ€:
NĂ€emme siis AppForest
-komponentin elementit ruohotaustalla, ja sen jÀlkeen kaiken projisoidun sisÀllön kirkkaan vihreÀn taustan pÀÀllÀ. Ja koska annoimme sovelluskomponenttimme sisÀllÀ arvon KingdomService
, kaikki sen sisÀllÀ oleva perii sen (paitsi yksi yksisarvinen, jolla on appInvader
-direktiivi).
Mutta mitÀ tapahtuu, jos annamme uuden arvon KingdomService
:lle ForestComponent
:n sisÀllÀ? Saako projisoitu sisÀltö (joka mÀÀriteltiin mallissa AppComponent
) myös tĂ€mĂ€n uuden arvon valtakunnalle? Vai pysyykö se edelleen đ§ valtakunnassa? Osaatko arvata?