Allt du behöver veta om ng-template, ng-content, ng-container och *ngTemplateOutlet i Angular

jul 1, 2021
admin

Det var en av de dagar då jag var upptagen med att arbeta på nya funktioner för mitt kontorsprojekt. Plötsligt fångade något min uppmärksamhet:

Final rendered DOM in Angular

När jag inspekterade DOM såg jag ngcontent som tillämpades på element av Angular. Hmm… om de innehåller elementen i den slutliga DOM, vad är då nyttan med <ng-container>? Då blev jag förvirrad mellan <ng-container> och <ng-content>.

I sökandet efter svaren på mina frågor upptäckte jag begreppet <ng-template>. Till min förvåning fanns det också *ngTemplateOutlet. Jag började min resa med att söka klarhet om två begrepp, men nu hade jag fyra stycken som lät nästan likadana!

Har du någonsin befunnit dig i den här situationen? Om ja, då är du på rätt plats. Så utan vidare ska vi ta dem en efter en.

<ng-template>

Som namnet antyder är <ng-template> ett mallelement som Angular använder tillsammans med strukturella direktiv (*ngIf, *ngFor, och anpassade direktiv).

Dessa mallelement fungerar endast i närvaro av strukturella direktiv. Angular omsluter värdelementet (som direktivet tillämpas på) inom <ng-template> och konsumerar <ng-template> i det färdiga DOM genom att ersätta det med diagnostiska kommentarer.

Konsumerar ett enkelt exempel på *ngIf:

Exempel 1- Angular-process för tolkning av strukturella direktiv

Ovan visas Angular-tolkningen av *ngIf. Angular placerar värdelementet som direktivet tillämpas på inom <ng-template> och behåller värdelementet som det är. Det slutliga DOM:et liknar det vi har sett i början av den här artikeln:

Exempel 1- Slutligt renderat DOM

Användning:

Vi har sett hur Angular använder <ng-template> men vad händer om vi vill använda det? Eftersom dessa element endast fungerar med ett strukturdirektiv kan vi skriva som:

Exempel 2- Användning av <ng-template>

Här är home en boolean egenskap för komponenten som är satt till true värde. Utfallet av ovanstående kod i DOM:

Exempel 2- Slutligt renderat DOM

Inget har renderats! 🙁

Men varför kan vi inte se vårt meddelande även efter att ha använt <ng-template> korrekt med ett strukturdirektiv?

Detta var det förväntade resultatet. Som vi redan har diskuterat ersätter Angular <ng-template> med diagnostiska kommentarer. Utan tvekan skulle ovanstående kod inte generera något fel, eftersom Angular är helt okej med ditt användningsfall. Du skulle aldrig få veta vad som exakt hände bakom kulisserna.

Låt oss jämföra de två ovanstående DOM:

Exempel 1 vs. Exempel 2

Om du tittar noga finns det en extra kommentarsflagga i den slutliga DOM:n i exempel 2. Koden som Angular tolkade var:

Angular tolkningsprocess för exempel 2

Angular lindade in din värd <ng-template> i en annan <ng-template> och konverterade inte bara den yttre <ng-template> till diagnostiska kommentarer utan även den inre! Därför kunde du inte se något av ditt meddelande.

För att bli av med detta finns det två sätt att få önskat resultat:

Korrekt användning av <ng-template>

Metod 1:

Med den här metoden förser du Angular med det avsugade formatet som inte behöver behandlas ytterligare. Den här gången omvandlar Angular bara <ng-template> till kommentarer, men lämnar innehållet inuti det orört (de finns inte längre inuti någon <ng-template> som i det tidigare fallet). Innehållet kommer alltså att återges korrekt.

Om du vill veta mer om hur du använder det här formatet med andra strukturella direktiv hänvisar vi till den här artikeln.

Metod 2:

Det här är ett ganska osynligt format och används sällan (med två syskon <ng-template>). Här ger vi en mallreferens till *ngIf i dess then för att tala om vilken mall som ska användas om villkoret är sant.

Användning av flera <ng-template> på det här sättet rekommenderas inte (du kan använda <ng-container> i stället) eftersom detta inte är vad de är avsedda för. De används som en behållare för mallar som kan återanvändas på flera ställen. Vi kommer att täcka mer om detta i ett senare avsnitt av den här artikeln.

<ng-container>

Har du någonsin skrivit eller sett kod som liknar den här:

Exempel 1

Anledningen till att många av oss skriver den här koden är oförmågan att använda flera strukturella direktiv på ett enda värdelement i Angular. Nu fungerar den här koden bra men den introducerar flera extra tomma <div> i DOM om item.id är ett falskt värde som kanske inte behövs.

Exempel 1- Slutligt renderad DOM

Man kanske inte oroar sig för ett enkelt exempel som detta, men för en stor applikation som har en komplex DOM (för att visa tiotusentals data) kan det här bli besvärligt eftersom elementen kan ha lyssnare kopplade till dem som fortfarande kommer att finnas kvar i DOM och lyssna på händelser.

Vad är ännu värre är nivån av nesting som du måste göra för att applicera din styling (CSS)!

Bild från: Inside Unbounce

Ingen fara, vi har <ng-container> till undsättning!

Angular <ng-container> är ett grupperingselement som inte stör stilar eller layout eftersom Angular inte lägger det i DOM.

Så om vi skriver vårt exempel 1 med <ng-container>:

Exempel 1 med <ng-container>

Vi får det slutliga DOM som:

Slutligt renderat DOM med <ng-container>

Se att vi blev av med de tomma <div>s. Vi bör använda <ng-container> när vi bara vill tillämpa flera strukturella direktiv utan att införa något extra element i vårt DOM.

För mer information hänvisar vi till dokumentationen. Det finns ett annat användningsområde där det används för att injicera en mall dynamiskt i en sida. Jag kommer att täcka detta användningsfall i det sista avsnittet av den här artikeln.

<ng-content>

De används för att skapa konfigurerbara komponenter. Detta innebär att komponenterna kan konfigureras beroende på användarens behov. Detta är välkänt som Content Projection (innehållsprojektion). Komponenter som används i publicerade bibliotek använder <ng-content> för att göra sig konfigurerbara.

Konsultera en enkel <project-content>-komponent:

Exempel 1- <project-content> definition
Innehållsprojektion med <project-content> komponent

Helt HTML-innehållet som skickas inom öppnings- och stängningsmarkerna för <project-content>-komponenten är det innehåll som ska projiceras. Detta är vad vi kallar Content Projection. Innehållet kommer att återges inuti <ng-content> inom komponenten. Detta gör det möjligt för konsumenten av <project-content>-komponenten att skicka en anpassad sidfot inom komponenten och kontrollera exakt hur de vill att den ska återges.

Multiple Projections:

Vad händer om du kan bestämma vilket innehåll som ska placeras var? Istället för att varje innehåll projiceras i en enda <ng-content> kan du också styra hur innehållet ska projiceras med select-attributet för <ng-content>. Det krävs en elementväljare för att bestämma vilket innehåll som ska projiceras inuti en viss <ng-content>.

Så här går det till:

Exempel 2- Projicering av flera innehåll med uppdaterad <project-content>

Vi har modifierat <project-content>-definitionen för att kunna utföra projicering av flera innehåll. Med attributet select väljer vi vilken typ av innehåll som ska återges i en viss <ng-content>. Här har vi först select för att återge elementet rubrik h1. Om det projicerade innehållet inte har något h1-element kommer det inte att återge något. På samma sätt söker den andra select efter ett div. Resten av innehållet återges i den sista <ng-content> utan select.

Kallandet av komponenten kommer att se ut så här:

Exempel 2- Kallande av <project-content>-komponenten i den överordnade komponenten

*ngTemplateOutlet

…De används som en behållare till mallar som kan återanvändas på flera ställen. Vi kommer att täcka mer om detta i ett senare avsnitt av den här artikeln.
…Det finns ett annat användningsfall där den används för att injicera en mall dynamiskt i en sida. Jag kommer att täcka detta användningsfall i det sista avsnittet av den här artikeln.

Detta är avsnittet där vi kommer att diskutera de två punkter som nämndes tidigare. *ngTemplateOutlet används för två scenarier – för att infoga en gemensam mall i olika delar av en vy oberoende av slingor eller villkor och för att göra en mycket konfigurerad komponent.

Mallåteranvändning av mallar:

Tänk på en vy där du måste infoga en mall på flera ställen. Till exempel en företagslogotyp som ska placeras på en webbplats. Vi kan uppnå detta genom att skriva mallen för logotypen en gång och återanvända den överallt i vyn.

Följande är kodutdraget:

Exempel 1- Återanvändning av mallar

Som du kan se har vi bara skrivit mallen för logotypen en gång och använt den tre gånger på samma sida med en enda kodrad!

*ngTemplateOutlet tar också emot ett kontextobjekt som kan överföras för att anpassa den gemensamma mallutgången. Mer information om kontextobjektet finns i den officiella dokumentationen.

Anpassningsbara komponenter:

Det andra användningsområdet för *ngTemplateOutlet är starkt anpassade komponenter. Tänk på vårt tidigare exempel på <project-content>komponent med vissa ändringar:

Exempel 2- Skapa anpassningsbar komponent, project-content.html

Ovan är den modifierade versionen av <project-content>komponenten som accepterar tre inmatningsegenskaper – headerTemplate, bodyTemplate, footerTemplate. Följande är utdraget för project-content.ts:

Exempel 2- Skapa en anpassningsbar komponent, project-content.ts

Vad vi försöker uppnå här är att visa rubrik, kropp och sidfot som de tas emot från den överordnade komponenten <project-content>. Om någon av dem inte tillhandahålls kommer vår komponent att visa standardmallen i stället. På så sätt skapas en mycket anpassad komponent.

För att använda vår nyligen modifierade komponent:

Exempel 2- Användning av den nyligen modifierade komponenten <project-content>

Det här är hur vi kommer att skicka template refs till vår komponent. Om någon av dem inte skickas kommer komponenten att återge standardmallen.

ng-content vs. *ngTemplateOutlet

Båda hjälper oss att uppnå mycket skräddarsydda komponenter, men vilken ska vi välja och när?

Det går tydligt att se att *ngTemplateOutlet ger oss lite mer makt att visa standardmallen om ingen tillhandahålls.

Detta är inte fallet med ng-content. Den visar innehållet som det är. Med hjälp av attributet select kan du dela upp innehållet och återge det på olika ställen i vyn. Det går inte att återge innehållet på villkor inom ng-content. Du måste visa det innehåll som tas emot från föräldern utan möjlighet att fatta beslut baserat på innehållet.

Valet att välja mellan de två beror dock helt och hållet på ditt användningsfall. Nu har vi åtminstone ett nytt vapen *ngTemplateOutlet i vår arsenal som ger mer kontroll över innehållet utöver funktionerna i ng-content!

Lämna ett svar

Din e-postadress kommer inte publiceras.