Всичко, което трябва да знаете за ng-template, ng-content, ng-container и * ngTemplateOutlet в Angular

Това беше един от онези дни, когато бях зает да работя върху нови функции за моя офис проект. Изведнъж нещо привлече вниманието ми:

Докато инспектирахме DOM, видях как ngcontentсе прилага върху елементи от Angular. Хм ... ако те съдържат елементите в окончателния DOM, тогава каква е ползата от тях ? По това време се обърках между и .

В стремежа си да знам отговорите на въпросите си открих концепцията за . За моя изненада също имаше *ngTemplateOutlet. Започнах пътуването си, търсейки яснота за две концепции, но сега имах четири от тях, звучащи почти еднакво!

Били ли сте някога в тази ситуация? Ако отговорът е да, тогава сте на правилното място. Така че, без повече шум, нека ги вземем един по един.

1.

Както подсказва името на е шаблон елемент, който ъглови употреби със структурни директиви ( *ngIf, *ngFor, [ngSwitch]и потребителски директиви).

Тези елементи на шаблона работят само при наличието на структурни директиви . Angular обгръща хост елемента (към който се прилага директивата) вътреи консумирав готовия DOM, като го замества с диагностични коментари.

Помислете за прост пример за *ngIf:

По-горе е показано ъгловата интерпретация на *ngIf. Angular поставя хост елемента, към който се прилага директивата, и поддържа хоста такъв, какъвто е. Последният DOM е подобен на това, което видяхме в началото на тази статия:

Употреба:

Видяхме как Angular използва, но какво, ако искаме да го използваме? Тъй като тези елементи работят само със структурна директива, можем да запишем като:

Тук homeима booleanсвойство на компонента, зададен на trueстойност. Изходът на горния код в DOM:

Нищо не е оказано! :(

Но защо не можем да видим нашето послание дори след правилното използване със структурна директива?

Това беше очакваният резултат. Както вече обсъдихме, Angular заменя с диагностични коментари. Без съмнение горният код няма да генерира никаква грешка, тъй като Angular е напълно добре с вашия случай на употреба. Никога не бихте разбрали какво точно се е случило зад кулисите.

Нека да сравним горните две DOM, които са представени от Angular:

Ако наблюдавате внимателно, има един допълнителен маркер за коментар във финалния DOM на пример 2 . Кодът, който интерпретира Angular е:

Angular опакова вашия хост в друг и преобразува не само външния в диагностични коментари, но и вътрешния! Ето защо не можете да видите нито едно от съобщенията си.

За да се отървете от това, има два начина да получите желания резултат:

Метод 1:

По този метод предоставяте на Angular де-захарирания формат, който не се нуждае от допълнителна обработка. Този път Angular ще конвертира само в коментари, но оставя съдържанието вътре в него недокоснато (те вече не са вътре, както в предишния случай). По този начин той ще изобрази съдържанието правилно.

За да научите повече за това как да използвате този формат с други структурни директиви, вижте тази статия.

Метод 2:

Това е доста невиждан формат и рядко се използва (използвайки двама братя и сестри ). Тук даваме препратка към шаблон *ngIfв него, thenза да му кажем кой шаблон трябва да се използва, ако условието е вярно.

Използването на множество такива не се препоръчва ( вместо това можете да използвате ), тъй като това не е предназначено за тях. Те се използват като контейнер за шаблони, които могат да бъдат използвани повторно на няколко места. Ще разгледаме повече за това в следващ раздел на тази статия.

2.

Били ли сте някога писали или виждали код, подобен на този:

Причината, поради която много от нас пишат този код, е невъзможността да се използват множество структурни директиви върху един хостов елемент в Angular. Сега този код работи добре, но въвежда няколко допълнителни празни в DOM, ако item.idе фалшива стойност, която може да не се изисква.

Човек може да не е загрижен за прост пример като този, но за огромно приложение, което има сложен DOM (за показване на десетки хиляди данни), това може да стане обезпокоително, тъй като елементите могат да имат присъединени слушатели, които все още ще бъдат там в DOM слушане на събития.

Още по-лошото е нивото на влагане, което трябва да направите, за да приложите стила си (CSS)!

Без притеснения, ние трябва да спасим!

Angular е елемент за групиране, който не пречи на стилове или оформление, защото Angular не го поставя в DOM .

Така че, ако напишем нашия пример 1 с :

Получаваме окончателния DOM като:

Вижте, че се отървахме от тези празни s. Трябва да използваме, когато просто искаме да приложим множество структурни директиви, без да въвеждаме допълнителен елемент в нашия DOM.

За повече информация вижте документите. Има и друг случай на използване, при който той се използва за динамично инжектиране на шаблон в страница. Ще разгледам този случай на употреба в последния раздел на тази статия.

3.

Те се използват за създаване на конфигурируеми компоненти. Това означава, че компонентите могат да бъдат конфигурирани в зависимост от нуждите на потребителя. Това е добре известно като Прожекция на съдържание . Компонентите, които се използват в публикуваните библиотеки, използват, за да се конфигурират.

Помислете за прост компонент:

HTML съдържанието, предадено в отварящите и затварящите тагове на компонента, е съдържанието, което се проектира. Това наричаме Прожекция на съдържание . Съдържанието ще се изобрази вътре в компонента. Това позволява на потребителя на компонента да предава всеки потребителски колонтитул в компонента и да контролира как точно иска да бъде изобразен.

Множество проекции:

Ами ако можете да решите кое съдържание къде да бъде поставено? Вместо всяко съдържание, проектирано вътре в един , можете също да контролирате как съдържанието ще се проектира с selectатрибута . Необходим е селектор на елементи, за да се реши кое съдържание да се проектира вътре в даден .

Ето как:

Променихме дефиницията, за да извършим прожектиране на много съдържание. В selectатрибута избира вида на съдържанието, които ще бъдат предоставени вътре в частност . Тук първо трябва selectда изобразим заглавния h1елемент. Ако проектираното съдържание няма h1елемент, то няма да изобрази нищо. По същия начин вторият selectтърси a div. Останалото съдържание се изобразява в последното с не select.

Извикването на компонента ще изглежда така:

4. * ngTemplateOutlet

... Те се използват като контейнер за шаблони, които могат да бъдат използвани повторно на множество места. Ще разгледаме повече за това в следващ раздел на тази статия.

... Има и друг случай на употреба, при който той се използва за динамично инжектиране на шаблон в страница. Ще разгледам този случай на употреба в последния раздел на тази статия.

Това е разделът, в който ще обсъдим споменатите по-горе две точки. *ngTemplateOutletсе използва за два сценария - за вмъкване на общ шаблон в различни секции на изглед, независимо от контури или състояние и за създаване на силно конфигуриран компонент.

Повторно използване на шаблона:

Помислете за изглед, в който трябва да вмъкнете шаблон на няколко места. Например фирмено лого, което да бъде поставено в уебсайт. Можем да го постигнем, като напишем шаблона за логото веднъж и го използваме отново навсякъде в изгледа.

Следва кодовият фрагмент:

Както можете да видите, ние просто написахме шаблона за лого веднъж и го използвахме три пъти на една и съща страница с един ред код!

*ngTemplateOutletсъщо така приема контекстен обект, който може да бъде предаден за персонализиране на изхода на общия шаблон. За повече информация относно контекстния обект вижте официалните документи.

Персонализируеми компоненти:

Вторият случай на употреба *ngTemplateOutletе силно персонализирани компоненти. Помислете за предишния ни пример за компонент с някои модификации:

Над е модифицирана версия на компонент, който приема три входни имоти -  headerTemplate, bodyTemplate, footerTemplate. Следва фрагментът за project-content.ts:

Това, което се опитваме да постигнем тук, е да покажем заглавката, тялото и долния колонтитул, получени от родителския компонент на . Ако някой от тях не е предоставен, нашият компонент ще покаже шаблона по подразбиране на негово място. По този начин, създавайки силно персонализиран компонент.

За да използвате наскоро модифицирания ни компонент:

Ето как ще предадем референтните шаблони на нашия компонент. Ако някой от тях не бъде предаден, тогава компонентът ще изобрази шаблона по подразбиране.

ng-content vs. * ngTemplateOutlet

И двамата ни помагат да постигнем силно персонализирани компоненти, но кой да изберем и кога?

Ясно може да се види, че *ngTemplateOutletни дава малко повече сила да показваме шаблона по подразбиране, ако не е предоставен такъв.

Това не е така при ng-content. Той прави съдържанието такова, каквото е. Най-много можете да разделите съдържанието и да го изобразите на различни места във вашия изглед с помощта на selectатрибут. Не можете условно да изобразите съдържанието в него ng-content. Трябва да показвате съдържанието, получено от родителя, без никакви средства за вземане на решения въз основа на съдържанието.

Изборът на избор между двете обаче зависи изцяло от вашия случай на употреба. Поне сега имаме ново оръжие *ngTemplateOutletв нашия арсенал, което осигурява повече контрол върху съдържанието в допълнение към характеристиките на ng-content!