温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

angular4 ng-content中隐藏的内容是什么

发布时间:2021-08-12 09:43:00 来源:亿速云 阅读:95 作者:小新 栏目:web开发

小编给大家分享一下angular4 ng-content中隐藏的内容是什么,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

Simple example

在本文中我们使用一个示例,来演示不同的方式实现内容投影。由于许多问题与Angular 中的组件生命周期相关,因此我们的主要组件将显示一个计数器,用于展示它已被实例化的次数:

import { Component } from '@angular/core';

let instances = 0;

@Component({
 selector: 'counter',
 template: '<h2>{{this.id}}</h2>'
})
class Counter {
 id: number;
 
 constructor() {
  this.id = ++instances;
 }
}

上面示例中我们定义了 Counter 组件,组件类中的 id 属性用于显示本组件被实例化的次数。接着我们继续定义一个 Wrapper 组件:

import { Component } from '@angular/core';

@Component({
 selector: 'wrapper',
 template: `
  <div class="box">
   <ng-content></ng-content>
  </div>
 `
})
class Wrapper {}

现在我们来验证一下效果:

<wrapper>
 <counter></counter>
 <counter></counter>
 <counter></counter>
</wrapper>

Targeted projection

有时你希望将包装器的不同子项投影到模板的不同部分。为了处理这个问题, <ng-content> 支持一个 select 属性,可以让你在特定的地方投射具体的内容。该属性支持 CSS 选择器(my-element,.my-class,[my-attribute],...)来匹配你想要的内容。如果 ng-content 上没有设置 select 属性,它将接收全部内容,或接收不匹配任何其他 ng-content 元素的内容。长话短说:

import { Component } from '@angular/core';

@Component({
 selector: 'wrapper',
 template: `
 <div class="box red">
  <ng-content></ng-content>
 </div>
 <div class="box blue">
  <ng-content select="counter"></ng-content>
 </div>
 `,
 styles: [`
  .red {background: red;}
  .blue {background: blue;}
 `]
})
export class Wrapper { }

上面示例中,我们引入了 select 属性,来选择投射的内容:

<wrapper>
 <span>This is not a counter</span>
 <counter></counter>
</wrapper>

上述代码成功运行后,counter 组件被正确投影到第二个蓝色框中,而 span 元素最终会在全部红色框中。请注意,目标 ng-content 会优先于 catch-all,即使它在模板中的位置靠后。

ngProjectAs

有时你的内部组件会被隐藏在另一个更大的组件中。有时你只需要将其包装在额外的容器中即可应用 ngIf ngSwitch。无论什么原因,通常情况下,你的内部组件不是包装器的直接子节点。为了演示上述情况,我们将 Counter 组件包装在一个 <ng-container> 中,看看我们的目标投影会发生什么:

<wrapper>
 <ng-container>
  <counter></counter>
 </ng-container>
</wrapper>

现在我们的 couter 组件会被投影到第一个红色框中。因为 ng-container 容器不再匹配 select="counter"。为了解决这个问题,我们必须使用 ngProjectAs 属性,它可以应用于任何元素上。具体如下:

<wrapper>
 <ng-container ngProjectAs="counter">
  <counter></counter>
 </ng-container>
</wrapper>

通过设置 ngProjectAs 属性,终于让我们的 counter 组件重回蓝色框的怀抱了。

Time to poke and prod

我们从一个简单的实验开始:将两个 <ng-content> 块放在我们的模板中,没有选择器。会出现什么情况?

页面中会显示一个或两个框,如果我们包含两个框,它们的内容是显示 1 和 1 或 1 和 2?

<div class="box red">
  <ng-content></ng-content>
</div>
<div class="box blue">
  <ng-content></ng-content>
</div>

答案是我们在最后一个 <ng-content> 中得到一个计数器,另一个是空的!在我们尝试解释为什么之前,让我们再来验证一个问题,即在 ng-content 指令的外层容器中添加 ngIf 指令:

import { Component } from '@angular/core';

@Component({
 selector: 'wrapper',
 template: `
  <button (click)="show = !show">
   {{ show ? 'Hide' : 'Show' }}
  </button>
  <div class="box" *ngIf="show">
   <ng-content></ng-content>
  </div>
 `
})
class Wrapper {
 show = true;
}

乍一看,似乎正常运行。但是如果你通过按钮进行切换操作,你会注意到计数器的值不会增加。这意味着我们的计数器组件只被实例化了一次 - 从未被销毁和重新创建。难道这是 ngIf 指令产生的问题,让我们测试一下 ngFor 指令,看看是否有同样的问题:

import { Component } from '@angular/core';

@Component({
 selector: 'wrapper',
 template: `
  <div class="box" *ngFor="let item of items">
   <ng-content></ng-content>
  </div>
 `
})
class Wrapper {
 items = [0, 0, 0];
}

以上代码运行后与我们使用多个 <ng-content> 的效果是一样的,只会显示一个计数器!为什么不按照我们的预期运行?

The explanation

<ng-content> 不会 "产生" 内容,它只是投影现有的内容。你可以认为它等价于 node.appendChild(el) 或 jQuery 中的 $(node).append(el) 方法:使用这些方法,节点不被克隆,它被简单地移动到它的新位置。因此,投影内容的生命周期将被绑定到它被声明的地方,而不是显示在地方。

这种行为有两个原因:期望一致性和性能。什么 "期望的一致性" 意味着作为开发人员,可以基于应用程序的代码,猜测其行为。假设我写了以下代码:

<div class="my-wrapper">
 <counter></counter>
</div>

很显然计数器将被实例化一次,但现在假如我们使用第三方库的组件:

<third-party-wrapper>
 <counter></counter>
</third-party-wrapper>

如果第三方库能够控制 counter 组件的生命周期,我将无法知道它被实例化了多少次。其中唯一方法就是查看第三方库的代码,了解它们的内部处理逻辑。将组件的生命周期被绑定到我们的应用程序组件而不是包装器的意义是,开发者可以掌控计数器只被实例化一次,而不用了解第三方库的内部代码。

性能的原因更为重要。因为 ng-content 只是移动元素,所以可以在编译时完成,而不是在运行时,这大大减少了实际应用程序的工作量。

The solution

为了让包装器能够控制其子元素的实例化,我们可以通过两种方式完成:在我们的内容周围使用 <ng-template> 元素,或者使用带有 "*" 语法的结构指令。为简单起见,我们将在示例中使用 <ng-template> 语法,我们的新应用程序如下所示:

<wrapper>
 <ng-template>
  <counter></counter>
 </ng-template>
</wrapper>

包装器不再使用 <ng-content>,因为它接收到一个模板。我们需要使用 @ContentChild 访问模板,并使用ngTemplateOutlet 来显示它:

@Component({
 selector: 'wrapper',
 template: `
  <button (click)="show = !show">
   {{ show ? 'Hide' : 'Show' }}
  </button>
  <div class="box" *ngIf="show">
   <ng-container [ngTemplateOutlet]="template"></ng-container>
  </div>
 `
})
class Wrapper {
 show = true;
 @ContentChild(TemplateRef) template: TemplateRef;
}

现在我们的 counter 组件,每当我们隐藏并重新显示时都正确递增!让我们再验证一下 *ngFor 指令:

@Component({
 selector: 'wrapper',
 template: `
  <div class="box" *ngFor="let item of items">
   <ng-container [ngTemplateOutlet]="template"></ng-container>
  </div>
 `
})
class Wrapper {
 items = [0, 0, 0];
 @ContentChild(TemplateRef) template: TemplateRef;
}

以上是“angular4 ng-content中隐藏的内容是什么”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI