It was one of those days when I was busy working on new features for my office project. All a sudden, something caught my attention:
那是我忙于为Office项目开发新功能的日子之一。 突然间,一些事情引起了我的注意:
While inspecting the DOM I saw the ngcontent
being applied on elements by Angular. Hmm… if they contain the elements in the final DOM, then what is the use of <ng-container>
? At that time I got confused between <ng-container>
and <ng-content>
.
在检查DOM时,我看到Angular将ngcontent
应用于元素。 嗯……如果它们包含最终DOM中的元素,那么<ng-container>
的用途是什么? 那时我在<ng-container>
和<ng-content>
之间感到困惑。
In the quest to know the answers to my questions I discovered the concept of <ng-template>
. To my surprise, there was also *ngTemplateOutlet
. I started my journey seeking clarity about two concepts but now I had four of them, sounding nearly the same!
为了了解我的问题的答案,我发现了<ng-template>
的概念。 令我惊讶的是,还有*ngTemplateOutlet
。 我开始探索两个概念的过程,但现在我有了四个,听起来几乎一样!
Have you ever been to this situation? If yes, then you are in the right place. So without further ado let’s take them one by one.
您去过这种情况吗? 如果是,那么您来对地方了。 因此,事不宜迟,让我们一一介绍。
As the name suggests the <ng-template>
is a template element that Angular uses with structural directives (*ngIf
, *ngFor
, [ngSwitch]
and custom directives).
顾名思义, <ng-template>
是Angular用于结构指令( *ngIf
, *ngFor
, [ngSwitch]
和自定义指令)的模板元素。
These template elements only work in the presence of structural directives. Angular wraps the host element (to which the directive is applied) inside <ng-template>
and consumes the <ng-template>
in the finished DOM by replacing it with diagnostic comments.
这些模板元素仅在存在结构指令的情况下起作用 。 Angular将主机元素(对其应用了指令)包装在<ng-template>
内部,并通过用诊断注释替换它来在完成的DOM中使用<ng-template>
。
Consider a simple example of *ngIf
:
考虑一个简单的*ngIf
示例:
Shown above is the Angular interpretation of *ngIf
. Angular puts the host element to which the directive is applied within <ng-template>
and keeps the host as it is. The final DOM is similar to what we have seen at the beginning of this article:
上面显示的是*ngIf
的角度解释。 Angular在<ng-template>
放置应用了指令的主机元素,并保持主机原样。 最终的DOM与我们在本文开头看到的类似:
We have seen how Angular uses <ng-template>
but what if we want to use it? As these elements work only with a structural directive, we can write as:
我们已经看到了Angular如何使用<ng-template>
但是如果我们想使用它呢? 由于这些元素仅适用于结构指令,因此我们可以这样写:
Here home
is a boolean
property of the component set to true
value. The output of the above code in DOM:
这里home
是设置为true
的组件的boolean
属性。 上面的代码在DOM中的输出:
Nothing got rendered! :(
什么都没呈现! :(
But why can’t we see our message even after using <ng-template>
correctly with a structural directive?
但是,为什么即使正确地将<ng-template>
与结构指令一起使用,我们也仍然看不到消息?
This was the expected result. As we have already discussed, Angular replaces the <ng-template>
with diagnostic comments. No doubt the above code would not generate any error, as Angular is perfectly fine with your use case. You would never get to know what exactly happened behind the scenes.
这是预期的结果。 正如我们已经讨论过的,Angular用诊断注释替换了<ng-template>
。 毫无疑问,上面的代码不会产生任何错误,因为Angular可以很好地满足您的用例。 您将永远不知道幕后到底发生了什么。
Let’s compare the above two DOMs that were rendered by Angular:
让我们比较一下Angular渲染的以上两个DOM:
If you watch closely, there is one extra comment tag in the final DOM of Example 2. The code that Angular interpreted was:
如果仔细观察, 示例2的最终DOM中会有一个额外的注释标签 。 Angular解释的代码是:
Angular wrapped up your host <ng-template>
within another <ng-template>
and converted not only the outer <ng-template>
to diagnostic comments but also the inner one! This is why you could not see any of your message.
Angular将您的主机<ng-template>
包裹在另一个<ng-template>
,不仅将外部的<ng-template>
为诊断注释,还转换了内部的注释! 这就是为什么您看不到任何消息的原因。
To get rid of this there are two ways to get your desired result:
要摆脱这种情况,可以通过两种方法获得所需的结果:
In this method, you are providing Angular with the de-sugared format that needs no further processing. This time Angular would only convert <ng-template>
to comments but leaves the content inside it untouched (they are no longer inside any <ng-template>
as they were in the previous case). Thus, it will render the content correctly.
在这种方法中,您将为Angular提供不需要进一步处理的减糖格式。 这次Angular只会将<ng-template>
转换为注释,而不会改变其中的内容(它们不再像以前的情况一样位于任何<ng-template>
中)。 因此,它将正确呈现内容。
To know more about how to use this format with other structural directives refer to this article.
要了解有关如何将此格式与其他结构性指令一起使用的更多信息,请参考本文 。
This is a quite unseen format and is seldom used (using two sibling <ng-template>
). Here we are giving a template reference to the *ngIf
in its then
to tell it which template should be used if the condition is true.
这是一种看不见的格式,很少使用(使用两个同级<ng-template>
)。 在这里,我们提供一个模板参考*ngIf
它then
告诉它应该使用哪个模板,如果条件为真。
Using multiple <ng-template>
like this is not advised (you could use <ng-container>
instead) as this is not what they are meant for. They are used as a container to templates that can be reused at multiple places. We will cover more on this in a later section of this article.
不建议这样使用多个<ng-template>
(您可以使用<ng-container>
),因为这不是它们的目的。 它们用作模板的容器,可以在多个地方重复使用。 我们将在本文的后面部分中对此进行详细介绍。
Have you ever written or seen code resembling this:
您是否曾经写过或看过类似以下的代码:
The reason why many of us write this code is the inability to use multiple structural directives on a single host element in Angular. Now this code works fine but it introduces several extra empty <div>
in the DOM if item.id
is a falsy value which might not be required.
我们许多人编写此代码的原因是无法在Angular中的单个主机元素上使用多个结构指令。 现在,这段代码可以正常工作,但是如果item.id
是一个伪造的值(可能不需要),则会在DOM中引入几个额外的空<div>
。
One may not be concerned for a simple example like this but for a huge application that has a complex DOM (to display tens of thousands of data) this might become troublesome as the elements might have listeners attached to them which will still be there in the DOM listening to events.
可能不关心像这样的简单示例,但是对于具有复杂DOM(以显示成千上万的数据)的大型应用程序来说,这可能会变得很麻烦,因为元素可能已附加了侦听器,而这些侦听器仍将保留在其中。 DOM监听事件。
What’s even worse is the level of nesting that you have to do to apply your styling (CSS)!
更糟糕的是,应用样式(CSS)所需的嵌套级别!
No worries, we have <ng-container>
to the rescue!
不用担心,我们有<ng-container>
来进行救援!
The Angular <ng-container>
is a grouping element that doesn't interfere with styles or layout because Angular doesn't put it in the DOM.
Angular <ng-container>
是一个分组元素,它不会干扰样式或布局,因为Angular 不会将其放在DOM中 。
So if we write our Example 1 with <ng-container>
:
因此,如果我们用<ng-container>
编写示例1 :
We get the final DOM as:
我们得到的最终DOM为:
See we got rid of those empty <div>
s. We should use <ng-container>
when we just want to apply multiple structural directives without introducing any extra element in our DOM.
看到我们摆脱了那些空的<div>
。 当我们只想应用多个结构性指令而不在DOM中引入任何额外元素时,应该使用<ng-container>
。
For more information refer to the docs. There’s another use case where it is used to inject a template dynamically into a page. I’ll cover this use case in the last section of this article.
有关更多信息,请参阅文档 。 还有另一种用例,用于将模板动态注入页面。 我将在本文的最后一部分介绍这种用例。
They are used to create configurable components. This means the components can be configured depending on the needs of its user. This is well known as Content Projection. Components that are used in published libraries make use of <ng-content>
to make themselves configurable.
它们用于创建可配置的组件。 这意味着可以根据其用户的需求来配置组件。 这就是众所周知的内容投影 。 已发布库中使用的组件使用<ng-content>
使其可配置。
Consider a simple <project-content>
component:
考虑一个简单的<project-content>
组件:
The HTML content passed within the opening and closing tags of <project-content>
component is the content to be projected. This is what we call Content Projection. The content will be rendered inside the <ng-content>
within the component. This allows the consumer of <project-content>
component to pass any custom footer within the component and control exactly how they want it to be rendered.
在<project-content>
组件的开始和结束标记中传递HTML内容是要投影的内容。 这就是我们所谓的Content Projection 。 内容将在组件的<ng-content>
内部呈现。 这允许<project-content>
组件的使用者在组件内传递任何自定义页脚,并精确控制他们希望如何呈现它。
What if you could decide which content should be placed where? Instead of every content projected inside a single <ng-content>
, you can also control how the contents will get projected with the select
attribute of <ng-content>
. It takes an element selector to decide which content to project inside a particular <ng-content>
.
如果您可以决定应将哪些内容放置在哪里呢? 除了在单个<ng-content>
投影的每个内容之外,您还可以使用<ng-content>
的select
属性控制如何投影<ng-content>
。 它需要一个元素选择器来决定要在特定的<ng-content>
投影哪些内容。
Here’s how:
这是如何做:
We have modified the <project-content>
definition to perform Multi-content projection. The select
attribute selects the type of content that will be rendered inside a particular <ng-content>
. Here we have first select
to render header h1
element. If the projected content has no h1
element it won’t render anything. Similarly the second select
looks for a div
. The rest of the content gets rendered inside the last <ng-content>
with no select
.
我们修改了<project-content>
定义以执行多内容投影。 select
属性选择将在特定<ng-content>
内部呈现的内容的类型。 在这里,我们首先select
呈现头h1
元素。 如果投影的内容没有h1
元素,则不会渲染任何内容。 同样,第二个select
查找div
。 其余内容将在没有select
呈现在最后一个<ng-content>
内部。
Calling the component will look like:
调用该组件将如下所示:
…They are used as a container to templates that can be reused at multiple places. We will cover more on this in a later section of this article.
…它们用作模板的容器,可以在多个地方重复使用。 我们将在本文的后面部分中对此进行详细介绍。
…They are used as a container to templates that can be reused at multiple places. We will cover more on this in a later section of this article.
…它们用作模板的容器,可以在多个地方重复使用。 我们将在本文的后面部分中对此进行详细介绍。
…They are used as a container to templates that can be reused at multiple places. We will cover more on this in a later section of this article.…There’s another use case where it is used to inject a template dynamically into a page. I’ll cover this use case in the last section of this article.
…它们用作模板的容器,可以在多个地方重复使用。 我们将在本文的后面部分中对此进行详细介绍。 …还有另一种用例,用于将模板动态注入页面。 我将在本文的最后一部分介绍这种用例。
This is the section where we will discuss the above two points mentioned before. *ngTemplateOutlet
is used for two scenarios — to insert a common template in various sections of a view irrespective of loops or condition and to make a highly configured component.
这是我们将讨论上述两点的部分。 *ngTemplateOutlet
用于两种情况-将通用模板插入视图的各个部分,而与循环或条件*ngTemplateOutlet
并制作一个高度配置的组件。
Consider a view where you have to insert a template at multiple places. For example, a company logo to be placed within a website. We can achieve it by writing the template for the logo once and reusing it everywhere within the view.
考虑一个视图,您必须在其中多个位置插入模板。 例如,要放置在网站内的公司徽标。 我们可以通过为徽标编写模板一次并在视图中的任何地方重复使用它来实现。
Following is the code snippet:
以下是代码段:
As you can see we just wrote the logo template once and used it three times on the same page with single line of code!
如您所见,我们只写了一次徽标模板,并在同一页面上用了一行代码就使用了它三遍!
*ngTemplateOutlet
also accepts a context object which can be passed to customize the common template output. For more information about the context object refer to the official docs.
*ngTemplateOutlet
还接受上下文对象,可以传递该上下文对象来自定义公共模板输出。 有关上下文对象的更多信息,请参考官方文档 。
The second use case for *ngTemplateOutlet
is highly customized components. Consider our previous example of <project-content>
component with some modifications:
*ngTemplateOutlet
的第二个用例是高度定制的组件。 考虑我们先前对<project-content>
组件进行一些修改的示例:
Above is the modified version of <project-content>
component which accepts three input properties — headerTemplate
, bodyTemplate
, footerTemplate
. Following is the snippet for project-content.ts
:
以上是的修改版本<project-content>
组分,其接受三个输入属性- headerTemplate
, bodyTemplate
, footerTemplate
。 以下是project-content.ts
的代码段:
What we are trying to achieve here is to show header, body and footer as received from the parent component of <project-content>
. If any one of them is not provided, our component will show the default template in its place. Thus, creating a highly customized component.
我们在这里想要实现的是显示从<project-content>
的父组件接收到的页眉,正文和页脚。 如果未提供任何一个,我们的组件将在其位置显示默认模板。 因此,创建了高度定制的组件。
To use our recently modified component:
要使用我们最近修改的组件:
This is how we are going to pass the template refs to our component. If any one of them is not passed then the component will render the default template.
这就是我们将模板引用传递给组件的方式。 如果未通过任何一个,则组件将呈现默认模板。
They both help us to achieve highly customized components but which to choose and when?
它们都可以帮助我们实现高度定制的组件,但是什么时候选择呢?
It can clearly be seen that *ngTemplateOutlet
gives us some more power of showing the default template if none is provided.
可以清楚地看到,如果未提供默认模板,则*ngTemplateOutlet
为我们提供更多显示默认模板的功能。
This is not the case with ng-content
. It renders the content as is. At the maximum you can split the content and render them at different locations of your view with the help of select
attribute. You cannot conditionally render the content within ng-content
. You have to show the content that is received from the parent with no means to make decisions based on the content.
ng-content
并非如此。 它按原样呈现内容。 您最多可以拆分内容,并在select
属性的帮助下将其呈现在视图的不同位置。 您不能在ng-content
内有条件地呈现ng-content
。 您必须显示从父级收到的内容,而不能根据该内容做出决定。
However, the choice of selecting among the two completely depends on your use case. At least now we have a new weapon *ngTemplateOutlet
in our arsenal which provides more control on the content in addition to the features of ng-content
!
但是,选择两者之间的选择完全取决于您的用例。 至少现在我们在武器库中有了一种新武器*ngTemplateOutlet
,除了ng-content
的功能外, *ngTemplateOutlet
提供了对内容的更多控制!