有条件的内容投影

90 阅读2分钟

angualr官网中内容投影一章中有条件的内容投影示例学习中遇到的一些问题

  1. 不管组件有没有使用 ng-content 元素,只要组件在使用过程中,有提供内容,那么这个内容就会被初始化

angualr 官网中内容投影一章中有条件的内容投影示例学习中遇到的一些问题(angular.cn/guide/conte…)

当时没看懂为什么不建议使用 ng-content 元素,以及这一小节中所提到的自定义指令 appExampleZippyContent 的用法没看懂

简述一下,就是当需要有条件的渲染或者多次渲染一段内容时,不建议使用ng-content,而应该使用该组件的时候,放入一段ng-template,这个ng-template元素中包含要渲染的内容

不建议使用 ng-content 的原因参考官网

为什么建议使用ng-template: 官网给出的原因是 可以让组件根据你想要的任何条件显式渲染内容,并可以进行多次渲染

多次渲染的话,是因为 ngTemplateOutlet 指令进行渲染 ng-template 的内容的,所以你可以多次使用这个指令进行渲染

<!-- example-zippy.template.html -->
<div *ngIf="expanded" [id]="contentId">
  <!-- 根据你想要的任何条件渲染 随意控制expand变量的值 -->
  <!-- 多次渲染这段templateRef -->
  <ng-container [ngTemplateOutlet]="content.templateRef"></ng-container>
  <ng-container [ngTemplateOutlet]="content.templateRef"></ng-container>
  <ng-container [ngTemplateOutlet]="content.templateRef"></ng-container>
</div>
// example-zippy.component.ts
import { AfterViewInit, Component, ContentChild, ElementRef } from "@angular/core";
import { ZippyContentDirective } from "../directives/example-zippy-content.directive";

@Component({
  selector: "app-example-zippy",
  templateUrl: "./example-zippy.component.html",
  styleUrl: "./example-zippy.component.less",
})
export class ExampleZippyComponent implements AfterViewInit {
  // 尽管在组件中未定义ng-content,但是由于在组件使用的时候,依旧提供了投影内容,并且使用这个指令,所以依然可以在ts中通过ContentChild获取到指令的实例
  @ContentChild(ZippyContentDirective) content!: ZippyContentDirective;

  expanded = true;
  contentId = "";

  ngAfterViewInit(): void {
    console.log(this.content.templateRef);
  }
}

appExampleZippyContent 指令的使用

// example-zippy.component.ts
@Directive({
  selector: "[appExampleZippyContent]",
})
export class ZippyContentDirective {
  constructor(public templateRef: TemplateRef<unknown>) {}
}

使用该组件

<app-example-zippy>
  <!-- 这段内嵌模板不会被初始化 -->
  <ng-template appExampleZippyContent>
    <h3>It depends on what you do with it.</h3>
  </ng-template>
</app-example-zippy>

小结:

  1. 在组件中即使不使用 ng-content,但是在使用组件的时候,依然提供了内容的话(dom/组件/指令),依旧可以通过 ContentChild 获取到提供的内容
  2. appExampleZippyContent 属性型指令的使用,这个可以将这段 ng-template 内嵌模板传递给指令内部的 templateRef,所以在 ts 文件中通过 ContentChild 获取到该指令的时候,可以通过指令的 templateRef 属性获取到这段模板,将它传递给 ngTemplateOutlet指令

注:试了一下,不使用这个属性型指令的话,直接通过ContentChild获取ng-template内嵌模板,然后将值传递给ngTemplateOutlet指令的话也可以

<div *ngIf="expanded" [id]="contentId">
  <ng-container [ngTemplateOutlet]="temTpl!"></ng-container>
  <ng-container [ngTemplateOutlet]="temTpl!"></ng-container>
  <ng-container [ngTemplateOutlet]="temTpl!"></ng-container>
</div>
import { AfterViewInit, Component, ContentChild, ElementRef, TemplateRef } from "@angular/core";
import { ZippyContentDirective } from "../directives/example-zippy-content.directive";
import { ReviewCasualComponent } from "../review/review-casual/review-casual.component";

@Component({
  selector: "app-example-zippy",
  templateUrl: "./example-zippy.component.html",
  styleUrl: "./example-zippy.component.less",
})
export class ExampleZippyComponent implements AfterViewInit {
  @ContentChild("temTpl", { static: true }) temTpl: TemplateRef<any> | undefined;

  expanded = true;
  contentId = "";

  ngAfterViewInit(): void {
    // console.log(this.content.templateRef);
    // console.log(this.titleEl);
    // console.log(this.rc);
    // this.rc?.sayHello();
  }
}
<app-example-zippy>
  <ng-template #temTpl>
    <h3>It depends on what you do with it.</h3>
  </ng-template>
</app-example-zippy>