ng-content 注意事项

ng-content标签不会“产生”内容,只是投影现有的内容,可以理解为移动现有的内容到指定位置,不会对现有的内容进行销毁和重建。

验证步骤:

  1. 我们定义父组件app-father和子组件app-son
  2. 父组件使用ng-content标签来投影整个子组件
  3. 父组件使用按钮来控制ng-content的显示和隐藏
  4. 在子组件生命周期函数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>

使用ContentChildContentChildren获取投影内容,类似ViewChildViewChildren

// 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>

Angular 插槽内容投影的使用(二)-小白菜博客

例子二:

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>