Kaikki mitä sinun tarvitsee tietää ng-template-, ng-content-, ng-container- ja *ngTemplateOutlet-ohjelmista Angularissa

heinä 1, 2021
admin

Se oli yksi niistä päivistä, jolloin työskentelin kiireisenä uusien ominaisuuksien parissa toimistoprojektissani. Yhtäkkiä jokin kiinnitti huomioni:

Final rendered DOM in Angular

Tarkastellessani DOM:ia huomasin, että Angular soveltaa ngcontent elementteihin. Hmm… jos ne sisältävät elementit lopullisessa DOMissa, niin mitä hyötyä on <ng-container>:sta? Tuolloin menin sekaisin <ng-container>:n ja <ng-content>:n välillä.

Haettaessa vastauksia kysymyksiini löysin <ng-template>:n käsitteen. Yllätyksekseni oli olemassa myös *ngTemplateOutlet. Aloitin matkani etsimällä selvyyttä kahteen käsitteeseen, mutta nyt niitä oli neljä, jotka kuulostivat lähes samalta!

Oletko koskaan ollut tässä tilanteessa? Jos kyllä, niin olet oikeassa paikassa. Käydään siis ilman muuta läpi ne yksi kerrallaan.

<ng-template>

Kuten nimestä voi päätellä, <ng-template> on template-elementti, jota Angular käyttää rakenteellisten direktiivien (*ngIf, *ngFor, ja mukautettujen direktiivien) kanssa.

Nämä template-elementit toimivat vain rakenteellisten direktiivien kanssa. Angular kietoo isäntäelementin (johon direktiiviä sovelletaan) <ng-template>:n sisälle ja kuluttaa <ng-template>:n valmiissa DOM:ssa korvaamalla sen diagnostisilla kommenteilla.

Tarkastellaan yksinkertaista esimerkkiä *ngIf:

Esimerkki 1. Angularin prosessi rakenteellisten direktiivien tulkitsemisessa

Yllä olevassa kuvassa on Angularin tulkinta *ngIf:sta. Angular sijoittaa isäntäelementin, johon direktiiviä sovelletaan, <ng-template>:n sisälle ja pitää isäntäelementin sellaisenaan. Lopullinen DOM on samanlainen kuin mitä olemme nähneet tämän artikkelin alussa:

Esimerkki 1- Lopullinen renderöity DOM

Käyttö:

Olemme nähneet, miten Angular käyttää <ng-template>:tä, mutta entä jos haluamme käyttää sitä? Koska nämä elementit toimivat vain rakenteellisen direktiivin kanssa, voimme kirjoittaa kuten:

Esimerkki 2- Käyttämällä <ng-template>

Tässä home on komponentin boolean-ominaisuus, joka on asetettu true-arvoon. Yllä olevan koodin tuloste DOM:ssa:

Esimerkki 2- Lopullinen renderöity DOM

Mitään ei renderöity! 🙁

Mutta miksi emme näe viestiämme, vaikka käytimme <ng-template> oikein rakenteellisen direktiivin kanssa?

Tämä oli odotettu tulos. Kuten olemme jo keskustelleet, Angular korvaa <ng-template>:n diagnostisilla kommenteilla. Epäilemättä yllä oleva koodi ei tuottaisi mitään virhettä, koska Angular on täysin kunnossa käyttötapauksesi kanssa. Et saisi koskaan tietää, mitä kulissien takana tarkalleen ottaen tapahtui.

Vertaillaanpa edellä mainittuja kahta DOM:ia, jotka Angular renderöi:

Esimerkki 1 vs. Esimerkki 2

Jos katsot tarkkaan, Esimerkin 2 lopullisessa DOM:ssa on yksi ylimääräinen kommenttitag. Angularin tulkitsema koodi oli:

Angularin tulkintaprosessi Esimerkille 2

Angular kietoi isäntäsi <ng-template> toisen <ng-template> sisälle ja muutti ulomman <ng-template>:n lisäksi myös sisemmän <ng-template>:n diagnostisiksi kommenteiksi! Tämän vuoksi et voinut nähdä mitään viestistäsi.

Tästä eroon pääsemiseksi on kaksi tapaa saada haluamasi lopputulos:

Korrekti käyttö <ng-template>

Tapa 1:

Tässä tavassa annat Angularille puretun muodon, joka ei vaadi enempää käsittelyä. Tällä kertaa Angular muuttaisi vain <ng-template>:n kommenteiksi, mutta jättää sen sisällä olevan sisällön koskemattomaksi (ne eivät ole enää minkään <ng-template>:n sisällä kuten edellisessä tapauksessa). Näin ollen se renderöi sisällön oikein.

Tietääksesi lisää siitä, miten tätä muotoa käytetään muiden rakenteellisten direktiivien kanssa, lue tämä artikkeli.

Menetelmä 2:

Tämä on melko näkymätön muoto ja sitä käytetään harvoin (käytetään kahta sisarus <ng-template>). Tässä annamme malliviittauksen *ngIf:lle sen then:ssä kertoaksemme, mitä mallia tulisi käyttää, jos ehto on tosi.

Moninkertaisten <ng-template>:ien käyttäminen tällä tavoin ei ole suositeltavaa (sen sijaan voisi käyttää <ng-container>:aa), koska niitä ei ole tarkoitettu tähän. Niitä käytetään säiliönä malleille, joita voidaan käyttää uudelleen useissa paikoissa. Käsittelemme asiaa tarkemmin tämän artikkelin myöhemmässä osassa.

<ng-container>

Oletko koskaan kirjoittanut tai nähnyt tätä muistuttavaa koodia:

Esimerkki 1

Syy siihen, miksi moni kirjoittaa tätä koodia, on se, että Angularissa ei voi käyttää useita rakenteellisia suuntalinjoja yhdellä isäntäelementillä. Nyt tämä koodi toimii hyvin, mutta se tuo DOM:iin useita ylimääräisiä tyhjiä <div>, jos item.id on falsy-arvo, jota ei välttämättä tarvita.

Esimerkki 1- Lopullinen renderöity DOM

Tällaisen yksinkertaisen esimerkin kohdalla ei ehkä ole huolissaan, mutta valtavassa sovelluksessa, jossa on monimutkainen DOM (kymmenien tuhansien tietojen näyttämiseen) tästä voi tulla hankalaa, koska elementteihin voi olla liitetty kuuntelijoita, jotka ovat edelleen DOM:ssa kuuntelemassa tapahtumia.

Mitä vielä pahempaa on sisäkkäisyyksien taso, jota joudut käyttämään tyylittelyn soveltamiseen (CSS:n)!

Kuva lähteestä: Inside Unbounce

Ei hätää, meillä on <ng-container> pelastamassa!

Angularin <ng-container> on ryhmittelyelementti, joka ei häiritse tyylejä tai ulkoasua, koska Angular ei laita sitä DOMiin.

Jos siis kirjoitamme Esimerkkimme 1:n <ng-container>:

Esimerkki 1:n <ng-container>

Kirjoitamme <ng-container>:

Esimerkkimme 1:n <ng-container>

Saatamme lopullisen DOM:n muotoon:

Final rendered DOM with <ng-container>

Se näkee, että pääsimme eroon noista tyhjistä <div>s. Meidän tulisi käyttää <ng-container> silloin, kun haluamme vain soveltaa useita rakenteellisia direktiivejä ilman, että DOM:iin tuodaan yhtään ylimääräistä elementtiä.

Lisätietoa löytyy dokumentista. On toinenkin käyttötapaus, jossa sitä käytetään lisäämään malli dynaamisesti sivulle. Käsittelen tätä käyttötapausta tämän artikkelin viimeisessä osassa.

<ng-content>

Sitä käytetään konfiguroitavien komponenttien luomiseen. Tämä tarkoittaa, että komponentteja voidaan konfiguroida sen käyttäjän tarpeiden mukaan. Tämä tunnetaan hyvin nimellä Content Projection. Julkaistuissa kirjastoissa käytettävät komponentit käyttävät <ng-content>:tä tehdäkseen itsensä konfiguroitaviksi.

Tarkastellaan yksinkertaista <project-content>-komponenttia:

Esimerkki 1- <project-content>-määrittely
Sisällön projisointi <project-content>-komponentin avulla

Esimerkki 1- <project-content>-määrittely

Projisoitavana sisältönä on HTML-sisällön sisältö, joka siirretään <project-content>-komponentin avaavissa ja sulkevissa tageissa. Tätä kutsutaan sisällön projisoinniksi. Sisältö renderöidään <ng-content>:n sisällä komponentin sisällä. Näin <project-content>-komponentin kuluttaja voi siirtää minkä tahansa mukautetun alatunnisteen komponentin sisälle ja kontrolloida tarkalleen, miten se halutaan renderöidä.

Multiple Projections:

Mitä jos voisit päättää, mikä sisältö sijoitetaan mihin? Sen sijaan, että jokainen sisältö projisoidaan yhden <ng-content>:n sisälle, voit myös hallita sitä, miten sisältö projisoidaan <ng-content>:n select-attribuutilla. Tarvitaan elementtivalitsin päättämään, mikä sisältö projisoidaan tietyn <ng-content>:n sisälle.

Tässä kerrotaan, miten:

Esimerkki 2- Monisisältöprojisointi päivitetyllä <project-content>

Muutimme <project-content>-määritelmää niin, että se suorittaa monisisältöprojektion. select-attribuutilla valitaan sisällön tyyppi, joka renderöidään tietyn <ng-content>:n sisällä. Tässä meillä on ensimmäinen select, jolla renderöidään otsikko h1-elementti. Jos projisoidussa sisällössä ei ole h1-elementtiä, se ei renderöi mitään. Vastaavasti toinen select etsii div-elementtiä. Loput sisällöstä renderöidään viimeisen <ng-content>:n sisällä, jossa ei ole select:tä.

Komponentin kutsuminen näyttää seuraavalta:

Esimerkki 2- <project-content>-komponentin kutsuminen emokomponentissa

*ngTemplateOutlet

… …Niitä käytetään säilytyspaikkana templaateille, joita voidaan käyttää uudelleen useissa paikoissa. Käsittelemme tätä lisää tämän artikkelin myöhemmässä osassa.
…On toinenkin käyttötapaus, jossa sitä käytetään mallinteen dynaamiseen injektointiin sivulle. Käsittelen tätä käyttötapausta tämän artikkelin viimeisessä osassa.

Tässä osiossa käsittelemme edellä mainittuja kahta kohtaa. *ngTemplateOutlet käytetään kahteen skenaarioon – yhteisen mallin lisäämiseen näkymän eri osioihin silmukoista tai ehdosta riippumatta ja pitkälle konfiguroidun komponentin tekemiseen.

Mallin uudelleenkäyttö:

Ajatellaan näkymää, jossa on lisättävä malli useaan paikkaan. Esimerkiksi yrityksen logo, joka sijoitetaan verkkosivustolle. Saamme sen aikaan kirjoittamalla logon mallin kerran ja käyttämällä sitä uudelleen kaikkialla näkymän sisällä.

Seuraava on koodinpätkä:

Esimerkki 1- Mallin uudelleenkäyttö

Kuten huomaat, kirjoitimme logon mallin vain kerran ja käytimme sitä kolme kertaa samalla sivulla yhdellä ainoalla koodirivillä!

*ngTemplateOutlet Hyväksyy myös konteksti-olion (context-olion), joka voidaan välittää, jotta voidaan muokata yhteistä mallin tuotosta. Lisätietoja kontekstiobjektista saat virallisista dokumenteista.

Räätälöitävät komponentit:

*ngTemplateOutlet:n toinen käyttötapaus on pitkälle räätälöidyt komponentit. Tarkastellaan aiempaa esimerkkiämme <project-content>komponentista muutamin muutoksin:

Esimerkki 2- Räätälöitävän komponentin tekeminen, project-content.html

Yllä on muokattu versio <project-content>-komponentista, joka hyväksyy kolme syöttöominaisuutta – headerTemplate, bodyTemplate, footerTemplate. Seuraavassa on project-content.ts:

Esimerkki 2- Mukautettavan komponentin tekeminen, project-content.ts

Tässä yritämme näyttää otsikon, rungon ja alatunnisteen sellaisina kuin ne on saatu <project-content>:n emokomponentilta. Jos jotakin niistä ei anneta, komponenttimme näyttää oletusmallin sen tilalla. Näin luomme erittäin räätälöidyn komponentin.

Käyttääksemme hiljattain muokattua komponenttiamme:

Esimerkki 2- Hiljattain muokatun komponentin <project-content>

Siten välitämme mallin viitteet komponentillemme. Jos mitään niistä ei välitetä, komponentti näyttää oletusmallin.

ng-content vs. *ngTemplateOutlet

Kummatkin auttavat meitä saamaan aikaan pitkälle räätälöityjä komponentteja, mutta kumman valitsemme ja milloin?

Voidaan selvästi nähdä, että *ngTemplateOutlet antaa meille enemmän valtaa näyttää oletusmallia, jos sellaista ei anneta.

Näin ei ole asianlaita, kun käytetään ng-content:a. Se renderöi sisällön sellaisenaan. Korkeintaan voit jakaa sisällön ja renderöidä ne näkymän eri kohdissa select-attribuutin avulla. Et voi renderöidä sisältöä ehdollisesti ng-content:n sisällä. Sinun on näytettävä vanhemmalta saamasi sisältö ilman keinoja tehdä päätöksiä sisällön perusteella.

Vaikka valinta näiden kahden välillä riippuu täysin käyttötapauksestasi. Ainakin nyt meillä on arsenaalissamme uusi ase *ngTemplateOutlet, joka tarjoaa enemmän hallintaa sisältöön ng-content:n ominaisuuksien lisäksi!

Vastaa

Sähköpostiosoitettasi ei julkaista.