Vše, co potřebujete vědět o ng-template, ng-content, ng-container a *ngTemplateOutlet v Angularu
Byl to jeden z těch dnů, kdy jsem pracoval na nových funkcích pro svůj kancelářský projekt. Najednou mě něco zaujalo:
Při kontrole DOMu jsem viděl, že na elementy Angular aplikuje ngcontent
. Hmm… pokud obsahují prvky ve finálním DOM, k čemu je potom <ng-container>
? Tehdy jsem si spletl <ng-container>
a <ng-content>
.
Při hledání odpovědí na své otázky jsem objevil pojem <ng-template>
. K mému překvapení existoval také *ngTemplateOutlet
. Na začátku své cesty jsem hledal jasno ve dvou pojmech, ale nyní jsem měl čtyři, které zněly téměř stejně!“
Jste někdy v takové situaci? Pokud ano, pak jste na správném místě. Pojďme si je tedy bez dalšího rozebírat jeden po druhém.
<ng-template>
Jak název napovídá, <ng-template>
je šablonový prvek, který Angular používá se strukturálními direktivami (*ngIf
, *ngFor
, a vlastními direktivami).
Tyto šablonové prvky fungují pouze v přítomnosti strukturálních direktiv. Angular zabalí hostitelský prvek (na který se směrnice aplikuje) dovnitř <ng-template>
a spotřebuje <ng-template>
v hotovém DOM tak, že jej nahradí diagnostickými komentáři.
Podívejte se na jednoduchý příklad *ngIf
:
Výše je zobrazena interpretace *ngIf
v jazyce Angular. Angular umístí hostitelský prvek, na který je směrnice aplikována, do <ng-template>
a hostitelský prvek ponechá tak, jak je. Konečný DOM je podobný tomu, který jsme viděli na začátku tohoto článku:
Použití:
Viděli jsme, jak Angular používá <ng-template>
, ale co když ho chceme použít? Protože tyto prvky pracují pouze se strukturální direktivou, můžeme je zapsat jako:
Zde home
je boolean
vlastnost komponenty nastavená na hodnotu true
. Výstup výše uvedeného kódu v DOM:
Nic se nevytvořilo! 🙁
Ale proč se naše zpráva nezobrazí ani po správném použití <ng-template>
se strukturní direktivou?“
Tento výsledek byl očekávaný. Jak jsme si již řekli, Angular nahrazuje <ng-template>
diagnostickými komentáři. Není pochyb o tom, že výše uvedený kód by nevygeneroval žádnou chybu, protože Angular je s vaším případem použití naprosto v pořádku. Nikdy byste se nedozvěděli, co přesně se stalo v zákulisí.
Pokusme se porovnat výše uvedené dva DOMy, které byly vykresleny systémem Angular:
Pokud budete pozorně sledovat, ve finálním DOMu příkladu 2 je jedna značka komentáře navíc. Kód, který Angular interpretoval, byl:
Angular zabalil váš hostitelský <ng-template>
do dalšího <ng-template>
a převedl na diagnostické komentáře nejen vnější <ng-template>
, ale také vnitřní! Proto jste nemohli vidět žádnou ze svých zpráv.
Abyste se toho zbavili, existují dva způsoby, jak dosáhnout požadovaného výsledku:
Způsob 1:
Při tomto způsobu poskytujete Angularu odcukrovaný formát, který nepotřebuje žádné další zpracování. Tentokrát by Angular pouze převedl <ng-template>
na komentáře, ale obsah uvnitř nich ponechá nedotčený (nejsou již uvnitř žádného <ng-template>
jako v předchozím případě). Obsah tedy vykreslí správně.
Chcete-li se dozvědět více o použití tohoto formátu s dalšími strukturálními direktivami, přečtěte si tento článek.
Způsob 2:
Tento formát je poměrně nevídaný a používá se jen zřídka (používá dva sourozenecké <ng-template>
). Zde dáváme odkaz na šablonu *ngIf
v jejím then
, abychom jí řekli, která šablona má být použita, pokud je podmínka pravdivá.
Používání více <ng-template>
tímto způsobem se nedoporučuje (místo toho můžete použít <ng-container>
), protože k tomu nejsou určeny. Používají se jako kontejner k šablonám, které lze opakovaně použít na více místech. Více se tomu budeme věnovat v pozdější části tohoto článku.
<ng-container>
Napsali jste někdy nebo viděli kód podobný tomuto:
Důvodem, proč mnozí z nás píší tento kód, je nemožnost použít více strukturálních direktiv na jeden hostitelský prvek v systému Angular. Nyní tento kód funguje dobře, ale zavádí do DOM několik dalších prázdných <div>
, pokud je item.id
falsy hodnota, která nemusí být nutná.
U jednoduchého příkladu, jako je tento, to nemusí nikoho trápit, ale u obrovské aplikace, která má složitý DOM (pro zobrazení desítek tisíc dat), to může začít být problematické, protože prvky mohou mít připojené posluchače, kteří tam budou stále v DOM naslouchat událostem.
Ještě horší je úroveň vnoření, kterou musíte udělat, abyste mohli použít stylování (CSS)!
Nic se neděje, na pomoc máme <ng-container>
!“
Angular <ng-container>
je seskupovací prvek, který nezasahuje do stylů ani rozvržení, protože jej Angular neumisťuje do DOM.
Zapíšeme-li tedy náš Příklad 1 s <ng-container>
:
, dostaneme výsledný DOM jako:
Vidíme, že jsme se zbavili těch prázdných <div>
. <ng-container>
Měli bychom použít <ng-container>
, když chceme pouze aplikovat více strukturních direktiv, aniž bychom do našeho DOM zavedli nějaký další prvek.
Další informace najdete v dokumentaci. Existuje ještě jeden případ použití, kdy se používá k dynamickému injektování šablony do stránky. Tomuto případu použití se budu věnovat v poslední části tohoto článku.
<ng-content>
Slouží k vytváření konfigurovatelných komponent. To znamená, že komponenty lze konfigurovat v závislosti na potřebách jejich uživatele. To je dobře známo jako promítání obsahu. Komponenty, které se používají v publikovaných knihovnách, využívají <ng-content>
, aby byly konfigurovatelné.
Podívejme se na jednoduchou komponentu <project-content>
:
Obsah HTML předaný v úvodní a závěrečné značce komponenty <project-content>
je obsah, který se má promítnout. Tomu říkáme promítání obsahu. Obsah bude vykreslen uvnitř <ng-content>
v rámci komponenty. To umožňuje spotřebiteli komponenty <project-content>
předat v rámci komponenty libovolnou vlastní patičku a přesně řídit, jak ji chce vykreslit.
Vícenásobné promítání:
Co kdybyste mohli rozhodnout, který obsah má být umístěn kde? Místo toho, aby se každý obsah promítal uvnitř jednoho atributu <ng-content>
, můžete také řídit, jak se bude obsah promítat pomocí atributu select
<ng-content>
. O tom, který obsah se promítne uvnitř konkrétního <ng-content>
, rozhoduje selektor prvku.
Podívejte se, jak na to:
Upravili jsme definici <project-content>
, aby bylo možné provádět promítání více obsahů. Atribut select
vybírá typ obsahu, který bude vykreslen uvnitř konkrétního <ng-content>
. Zde máme jako první select
pro vykreslení záhlaví prvku h1
. Pokud promítaný obsah nemá žádný prvek h1
, nebude se vykreslovat nic. Podobně druhý select
hledá prvek div
. Zbytek obsahu se vykreslí uvnitř posledního <ng-content>
bez select
.
Volání komponenty bude vypadat takto:
*ngTemplateOutlet
…Používají se jako kontejner na šablony, které lze opakovaně použít na více místech. Více se tomu budeme věnovat v některé z dalších částí tohoto článku.
…Existuje ještě jeden případ použití, kdy se používá k dynamickému injektování šablony do stránky. Tomuto případu použití se budu věnovat v poslední části tohoto článku.
V této části se budeme zabývat dvěma výše zmíněnými body. *ngTemplateOutlet
Používá se pro dva scénáře – pro vložení společné šablony do různých částí pohledu bez ohledu na smyčky nebo podmínky a pro vytvoření vysoce konfigurovatelné komponenty.
Znovupoužití šablony:
Považujte pohled, kde je třeba vložit šablonu na více místech. Například logo společnosti, které má být umístěno v rámci webové stránky. Toho můžeme dosáhnout tak, že šablonu pro logo napíšeme jednou a opakovaně ji použijeme všude v rámci zobrazení.
Následuje úryvek kódu:
Jak vidíte, šablonu pro logo jsme napsali pouze jednou a třikrát ji použili na téže stránce pomocí jediného řádku kódu!
*ngTemplateOutlet
Také přijímá kontextový objekt, který lze předat pro přizpůsobení společného výstupu šablony. Další informace o kontextovém objektu najdete v oficiálních dokumentech.
Přizpůsobitelné komponenty:
Druhým případem použití *ngTemplateOutlet
jsou vysoce přizpůsobitelné komponenty. Uvažujme náš předchozí příklad komponenty <project-content>
s určitými úpravami:
Výše je upravená verze komponenty <project-content>
, která přijímá tři vstupní vlastnosti – headerTemplate
, bodyTemplate
, footerTemplate
. Následuje úryvek pro project-content.ts
:
Co se zde snažíme dosáhnout, je zobrazení záhlaví, těla a zápatí tak, jak je obdržíme od nadřazené komponenty <project-content>
. Pokud některá z nich není poskytnuta, naše komponenta místo ní zobrazí výchozí šablonu. Vytvoříme tak vysoce přizpůsobenou komponentu.
Pro použití naší nedávno upravené komponenty:
Takto budeme předávat ref. šablony naší komponentě. Pokud některou z nich nepředáme, bude komponenta vykreslovat výchozí šablonu.
ng-content vs. *ngTemplateOutlet
Oboje nám pomůže dosáhnout vysoce přizpůsobené komponenty, ale kterou zvolit a kdy?
Je jasně vidět, že *ngTemplateOutlet
nám dává o něco větší sílu zobrazit výchozí šablonu, pokud žádná není poskytnuta.
To není případ ng-content
. Ta vykresluje obsah tak, jak je. Maximálně můžete obsah rozdělit a vykreslit je na různých místech zobrazení pomocí atributu select
. V rámci ng-content
nelze obsah podmíněně vykreslit. Musíte zobrazit obsah, který je přijat od nadřazeného prvku, bez možnosti rozhodování na základě obsahu.
Výběr výběru mezi těmito dvěma způsoby však zcela závisí na vašem případu použití. Alespoň teď máme v arzenálu novou zbraň *ngTemplateOutlet
, která kromě funkcí ng-content
poskytuje větší kontrolu nad obsahem!
.