Minden, amit tudnod kell az ng-template, ng-content, ng-container és *ngTemplateOutlet Angularban

júl 1, 2021
admin

Egyike volt azoknak a napoknak, amikor az irodai projektem új funkcióin dolgoztam. Egyszer csak valami felkeltette a figyelmemet:

A véglegesen renderelt DOM Angularban

A DOM vizsgálata közben láttam, hogy az ngcontent-ot az Angular alkalmazza az elemekre. Hmm… ha ezek tartalmazzák az elemeket a végleges DOM-ban, akkor mire jó a <ng-container>? Akkoriban összezavarodtam a <ng-container> és a <ng-content> között.

A kérdésemre adott válaszok keresése közben felfedeztem a <ng-template> fogalmát. Meglepetésemre ott volt a *ngTemplateOutlet is. Az utazásomat úgy kezdtem, hogy két fogalomról kerestem tisztánlátást, de most már négy ilyen fogalom volt, amelyek majdnem ugyanúgy hangzottak!

Voltál már valaha ilyen helyzetben? Ha igen, akkor jó helyen jársz. Szóval minden további nélkül vegyük sorra őket.

<ng-template>

Amint a neve is mutatja, a <ng-template> egy sablonelem, amelyet az Angular strukturális direktívákkal (*ngIf, *ngFor, és saját direktívákkal) együtt használ.

Ezek a sablonelemek csak strukturális direktívák jelenlétében működnek. Az Angular a gazdaelemet (amelyre a direktívát alkalmazza) <ng-template> belsejébe csomagolja, és a <ng-template>-t a kész DOM-ban diagnosztikai megjegyzésekkel helyettesítve fogyasztja el.

Nézzünk egy egyszerű példát a *ngIf-ra:

Példa 1- A szerkezeti direktívák értelmezésének Angular-folyamata

A fenti képen a *ngIf Angular értelmezése látható. Az Angular a <ng-template>-be helyezi a host elemet, amelyre a direktívát alkalmazza, a hostot pedig változatlanul megtartja. A végleges DOM hasonló ahhoz, amit a cikk elején láttunk:

Példa 1- Véglegesen renderelt DOM

Használat:

Láttuk, hogyan használja az Angular az <ng-template>-t, de mi van, ha mi szeretnénk használni? Mivel ezek az elemek csak szerkezeti direktívával működnek, így írhatjuk:

Példa 2- A <ng-template használata>

Itt home a komponens boolean tulajdonsága true értékre állítva. A fenti kód kimenete a DOM-ban:

Példa 2- Véglegesen renderelt DOM

Nem lett renderelve semmi! 🙁

De miért nem látjuk az üzenetünket még akkor sem, ha helyesen használjuk a <ng-template> szerkezeti direktívát?

Ez volt a várt eredmény. Ahogyan azt már megbeszéltük, az Angular a <ng-template>-t diagnosztikai megjegyzésekkel helyettesíti. Kétségtelen, hogy a fenti kód nem generálna semmilyen hibát, mivel az Angular tökéletesen megfelel az Ön felhasználási esetének. Soha nem tudnánk meg, hogy pontosan mi történt a színfalak mögött.

Versenyezzük össze a fenti két DOM-ot, amelyet az Angular renderelt:

Példa 1 vs. Példa 2

Ha jól figyelünk, a 2. példa végleges DOM-jában van egy extra komment tag. Az Angular által értelmezett kód:

Angular értelmezési folyamata a 2. példához

Az Angular becsomagolta a gazdatest <ng-template>-t egy másik <ng-template>-be, és nemcsak a külső <ng-template>-t, hanem a belsőt is diagnosztikai kommentárrá alakította! Ezért nem láthattad az üzenetedet.

Azért, hogy megszabadulj ettől, kétféleképpen érheted el a kívánt eredményt:

A <ng-template helyes használata>

1. módszer:

Ezzel a módszerrel megadod az Angularnak a lecsupaszított formátumot, amely nem igényel további feldolgozást. Ezúttal az Angular csak a <ng-template>-et alakítja át kommentárrá, de a benne lévő tartalmat érintetlenül hagyja (azok már nincsenek semmilyen <ng-template>-ben, mint az előző esetben). Így helyesen rendereli a tartalmat.

Azzal kapcsolatban, hogy hogyan használjuk ezt a formátumot más szerkezeti direktívákkal, olvassuk el ezt a cikket.

2. módszer:

Ez egy meglehetősen ritkán látott és ritkán használt formátum (két testvér <ng-template> használatával). Itt egy sablonreferenciát adunk a *ngIf-nak a then-ben, hogy megmondjuk neki, melyik sablont kell használni, ha a feltétel igaz.

A több <ng-template> ilyen módon történő használata nem javasolt (használhatnánk helyette <ng-container>-t), mivel nem erre szolgálnak. Olyan sablonok tárolására szolgálnak, amelyek több helyen újra felhasználhatók. Erről bővebben a cikk egy későbbi részében lesz szó.

<ng-container>

Írtál vagy láttál már ehhez hasonló kódot:

1. példa

Az ok, amiért sokan ezt a kódot írják, az az, hogy az Angularban nem lehet több szerkezeti direktívát használni egyetlen host elemen. Most ez a kód jól működik, de több extra üres <div>-et vezet be a DOM-ba, ha a item.id egy falsy érték, amire nem biztos, hogy szükség van.

Példa 1- Véglegesen renderelt DOM

Egy ilyen egyszerű példánál talán nem kell aggódni, de egy hatalmas alkalmazásnál, amely komplex DOM-mal rendelkezik (több tízezer adat megjelenítéséhez), ez problémás lehet, mivel az elemekhez lehet, hogy listenerek kapcsolódnak, amelyek még mindig ott lesznek a DOM-ban az eseményekre figyelve.

Ami még rosszabb, az a beágyazás szintje, amit a stílus (CSS) alkalmazásához kell tennünk!

Image from: Inside Unbounce

Nem kell aggódni, itt van a <ng-container> a segítségünkre!

Az Angular <ng-container> egy csoportosító elem, amely nem zavarja a stílusokat vagy az elrendezést, mert az Angular nem helyezi el a DOM-ban.

Így ha az 1. példánkat <ng-container>-val írjuk:

Példa 1 <ng-container>

A végleges DOM-ot a következőképpen kapjuk:

A végső renderelt DOM a <ng-container>

Láthatjuk, hogy megszabadultunk az üres <div>-ektől. A <ng-container>-t akkor kell használnunk, ha csak több szerkezeti direktívát akarunk alkalmazni anélkül, hogy bármilyen extra elemet bevezetnénk a DOM-unkba.

Bővebb információért olvassuk el a dokumentációt. Van egy másik felhasználási eset, amikor egy sablon dinamikus beillesztésére használjuk egy oldalba. Ezzel a felhasználási esettel a cikk utolsó részében foglalkozom.

<ng-content>

Konfigurálható komponensek létrehozására használják. Ez azt jelenti, hogy a komponensek a felhasználó igényeitől függően konfigurálhatók. Ezt Content Projection néven ismerjük. A közzétett könyvtárakban használt komponensek a <ng-content>-t használják, hogy konfigurálhatóvá tegyék magukat.

Nézzünk egy egyszerű <project-content> komponenst:

Példa 1- <project-content> definíció
Content Projection with <project-content> component

A <project-content> komponens nyitó és záró tagjeiben átadott HTML tartalom a kivetítendő tartalom. Ezt nevezzük tartalomvetítésnek. A tartalom a komponensben lévő <ng-content> belsejében kerül megjelenítésre. Ez lehetővé teszi a <project-content> komponens fogyasztójának, hogy bármilyen egyéni láblécet átadjon a komponensen belül, és pontosan szabályozza, hogy hogyan szeretné azt megjeleníteni.

Multiple Projections:

Mi lenne, ha eldönthetné, hogy melyik tartalom hova kerüljön? Ahelyett, hogy minden tartalom egyetlen <ng-content> belsejében vetülne ki, a <ng-content> select attribútumával azt is szabályozhatja, hogy a tartalom hogyan kerüljön kivetítésre. Egy elemválasztó segítségével dönti el, hogy melyik tartalom kerüljön kivetítésre egy adott <ng-content> belsejében.

Íme, hogyan:

Példa 2- Több tartalom kivetítése frissített <project-content>

Módosítottuk a <project-content> definíciót a több tartalom kivetítésének elvégzéséhez. A select attribútum kiválasztja a tartalom típusát, amely egy adott <ng-content>-n belül kerül megjelenítésre. Itt az első select a fejléc h1 elem renderelésére szolgál. Ha a kivetített tartalom nem tartalmaz h1 elemet, akkor nem fog renderelni semmit. Hasonlóképpen a második select egy div elemet keres. A tartalom többi része az utolsó <ng-content> belül kerül renderelésre, ha nincs select.

A komponens hívása így fog kinézni:

Példa 2- A <project-content> komponens hívása a szülő komponensben

*ngTemplateOutlet

…A több helyen újrafelhasználható sablonok tárolására szolgálnak. Erről bővebben a cikk egy későbbi részében lesz szó.
…Van egy másik felhasználási eset, amikor egy sablon dinamikus beillesztésére használjuk egy oldalba. Ezzel a felhasználási esettel a cikk utolsó részében foglalkozom.

Ez az a rész, ahol a fentebb említett két pontról lesz szó. *ngTemplateOutlet Két forgatókönyvhöz használjuk – egy közös sablon beillesztésére egy nézet különböző szakaszaiba ciklusoktól vagy feltételektől függetlenül, valamint egy magasan konfigurált komponens elkészítésére.

Sablon újrafelhasználása:

Gondoljunk egy olyan nézetre, ahol több helyen is be kell illesztenünk egy sablont. Például egy weboldalon belül elhelyezendő vállalati logó. Ezt úgy érhetjük el, hogy egyszer megírjuk a logó sablonját, és a nézeten belül mindenhol újra felhasználjuk.

A következő kódrészlet:

Példa 1- Sablon újrafelhasználás

Mint látható, a logó sablont csak egyszer írtuk meg, és egyetlen kódsorral háromszor használtuk ugyanazon az oldalon!

*ngTemplateOutlet Elfogad egy context objektumot is, amelyet átadhatunk a közös sablon kimenet testreszabásához. A kontextusobjektummal kapcsolatos további információkért olvassa el a hivatalos dokumentációt.

Csak testreszabható komponensek:

A *ngTemplateOutlet második felhasználási esete a nagymértékben testreszabható komponensek. Tekintsük korábbi példánkat a <project-content>komponensre néhány módosítással:

Példa 2- Testreszabható komponens készítése, project-content.html

Fentebb a <project-content> komponens módosított változata, amely három bemeneti tulajdonságot fogad el – headerTemplate, bodyTemplate, footerTemplate. Az alábbiakban a project-content.ts részlete következik:

Példa 2- Testreszabható komponens készítése, project-content.ts

Azt szeretnénk elérni, hogy a <project-content> szülő komponensből kapott fejléc, törzs és lábléc megjelenjen. Ha ezek közül bármelyik nincs megadva, a komponensünk az alapértelmezett sablont fogja megjeleníteni helyette. Így egy erősen testreszabott komponenst hozunk létre.

A nemrég módosított komponensünk használatához:

Példa 2- Az újonnan módosított <project-content>

Ezzel fogjuk átadni a sablonreferenciákat a komponensünknek. Ha bármelyiket nem adjuk át, akkor a komponens az alapértelmezett sablont fogja megjeleníteni.

ng-content vs. *ngTemplateOutlet

Mindkettő segít nekünk abban, hogy nagyon testreszabott komponenseket érjünk el, de melyiket válasszuk és mikor?

Egyértelműen látható, hogy a *ngTemplateOutlet némileg több lehetőséget ad nekünk az alapértelmezett sablon megjelenítésére, ha nincs megadva.

A ng-content esetében ez nem így van. Ez úgy jeleníti meg a tartalmat, ahogy van. Maximum a select attribútum segítségével feloszthatjuk a tartalmat és megjeleníthetjük a nézet különböző helyein. A ng-content-n belül nem tudja feltételesen megjeleníteni a tartalmat. A szülőktől kapott tartalmat kell megjelenítened, anélkül, hogy a tartalom alapján döntést hozhatnál.

Az, hogy a kettő közül mit választasz, teljesen a felhasználási esetedtől függ. Legalább most már van egy új fegyver *ngTemplateOutlet a fegyvertárunkban, amely a ng-content funkciói mellett nagyobb kontrollt biztosít a tartalom felett!

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

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