ng-content 注意事项
ng-content
标签不会“产生”内容,只是投影现有的内容,可以理解为移动现有的内容到指定位置,不会对现有的内容进行销毁和重建。
验证步骤:
- 我们定义父组件
app-father
和子组件app-son
- 父组件使用
ng-content
标签来投影整个子组件 - 父组件使用按钮来控制
ng-content
的显示和隐藏 - 在子组件生命周期函数
ngOnInit
,ngOnDestroy
打印内容验证
<!-- 父组件 father模板 -->
<div>
<header>头部</header>
<button (click)="handleShow()">{{show?'隐藏':'显示'}}</button>
<div *ngIf="show">
<ng-content></ng-content>
</div>
<footer>底部</footer>
</div>
// 父组件 father.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-father',
templateUrl: './father.component.html',
styleUrls: ['./father.component.less']
})
export class FatherComponent implements OnInit {
show = true;
constructor() { }
handleShow() {
this.show = !this.show
}
ngOnInit(): void { }
}
在子组件的相关生命周期函数ngOnInit
,ngOnDestroy
打印内容验证
// 子组件 son.component.ts
ngOnInit(): void {
console.log('组件初始化了')
}
ngOnDestroy(): void {
console.log('组件销毁了')
}
<app-father>
<app-son></app-son>
</app-father>
在控制台我们看到只打印出“组件初始化了”。当我们点击按钮进行切换操作时,就不再打印了,这说明app-son
组件只被实例化了一次,从未被销毁和重新创建。
获取投影内容
我们创建一个父组件AnimalComponent
<!-- animal.component.html -->
<h3>Animal</h3>
<hr>
<ng-content></ng-content>
<hr>
<h3>Animal</h3>
使用ContentChild
和ContentChildren
获取投影内容,类似ViewChild
和ViewChildren
// animal.component.ts
import { Component, OnInit, AfterContentInit, ContentChild, ContentChildren, QueryList } from '@angular/core';
import { RabbitComponent } from '../rabbit/rabbit.component';
import { CatComponent } from '../cat/cat.component';
@Component({
selector: 'app-animal',
templateUrl: './animal.component.html',
styleUrls: ['./animal.component.less']
})
export class AnimalComponent implements OnInit, AfterContentInit {
@ContentChild('rabbit') rabbit: RabbitComponent;
@ContentChildren(CatComponent) cats: QueryList<CatComponent>;
constructor() { }
ngOnInit(): void {
}
// 必须在生命周期AfterContentInit获取
ngAfterContentInit():void {
console.log('rabbit', this.rabbit)
this.cats.forEach(cat => console.log(cat))
}
}
创建子组件RabbitComponent
和子组件CatComponent
,并将他们用在父组件AnimalComponent
的内容投影上。
<app-animal>
<app-rabbit #rabbit></app-rabbit>
<app-cat></app-cat>
<app-cat></app-cat>
<app-cat></app-cat>
</app-animal>
控制台打印结果
有条件的内容投影
推荐使用ng-container
标签,因为该标签不需要渲染真实的 DOM 元素。
<ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>
<!-- 等同 -->
<ng-container [ngTemplateOutlet]="templateRefExp" [ngTemplateOutletContext]="contextExp"></ng-container>
参数 | 类型 | 说明 |
---|---|---|
templateRefExp | TemplateRef | null | 一个字符串,用于定义模板引用以及模板的上下文对象 |
contextExp | Object | null | 是一个对象,该对象的键名将可以在局部模板中使用 let 声明中进行绑定。在上下文对象中使用 $implicit 为键名时,将把它作为默认值。 |
ng-template
标签的#ID
会匹配templateRefExp
,将ng-template
标签的内容嵌入到指定的ngTemplateOutlet
中。
例子一:
<header>头部</header>
<main>
<h3>内容:</h3>
<ng-container [ngTemplateOutlet]="greet"></ng-container>
</main>
<footer>底部</footer>
<ng-template #greet>
<div>
<h4>hi!</h4>
<h4>hello my dear friend!</h4>
</div>
</ng-template>
例子二:
myContext
是自定义的对象,$implicit
为固定键名, 其值为默认值。myContext
其他属性可通过在ng-template
标签上通过let-自定义属性名="目标属性名"
来设置并在可嵌入模板中使用。
<header>头部</header>
<main>
<h3>内容:</h3>
<ng-container *ngTemplateOutlet="greet;context: myContext"></ng-container>
</main>
<footer>底部</footer>
<ng-template #greet let-name="name" let-myAge="age" let-default>
<div>
<h4>hi {{name}}, you're {{myAge}} years old</h4>
<h4>hello my dear friend {{default}}</h4>
</div>
</ng-template>
myContext = { $implicit: 'Defalut value', name: '诸葛亮', age: 20 };
例子三:
ng-template
模板在外部
<header>头部</header>
<main>
<h3>内容:</h3>
<ng-container *ngTemplateOutlet="greet;context: myContext"></ng-container>
</main>
<footer>底部</footer>
import { Component, ContentChild, TemplateRef } from '@angular/core';
@Component({
selector: 'app-father',
templateUrl: './father.component.html',
styleUrls: ['./father.component.less']
})
export class AboutComponent {
@ContentChild(TemplateRef) greet: TemplateRef<any>;
myContext = { $implicit: 'Defalut value', name: '诸葛亮', age: 20 };
constructor() { }
}
<app-father>
<ng-template let-name="name" let-default>
{{name}}
<app-son [property]="default"></app-son>
</ng-template>
</app-father>