Angular 内容投影实际上就是在组件设置一个占位符,引用组件的时候可以拿自定义元素替代占位符以实现灵活自定义。
1. 单槽投影
- 创建一个组件, 在组件模板中,添加 <ng-content> 元素。
import { Component } from '@angular/core'; @Component({ selector: 'app-zippy-basic', template: ` <h2>Single-slot content projection</h2> <ng-content></ng-content> ` }) export class ZippyBasicComponent {}
- 使用组建的地方可以自定义插槽内容
<app-zippy-basic> <p>Is content projection cool?</p> </app-zippy-basic>
2. 多槽投影
- 多槽投影顾名思义: 一个组件可以接收多个插槽,这意味着接收的多个元素要与多个插槽需要有一一对应关系,这个对应关系可以通过选择器来实现。
- 创建组件,在模板中添加具有选择器的 <ng-content> 元素
import { Component } from '@angular/core'; @Component({ selector: 'app-zippy-multislot', template: ` <div style="border: 1px solid;"> <!-- 多插槽 --> <!-- 可以使用html 标签,css class,自定义组件名称,自定义属性名称来锁定投影位置 --> <ng-content select="h3"></ng-content> <ng-content select=".my-class"></ng-content> <ng-content select="app-my-hello"></ng-content> <ng-content select="[content]"></ng-content> </div> <div style="border: 1px solid;"> <!-- 投影子元素 --> <!-- 使用ng-container来包裹子元素,减少不必要的dom层,类似vue中的template --> <ng-content select="question"></ng-content> </div> ` }) export class ZippyMultislotComponent {}
- 使用选择器属性来一一匹配传入元素
<app-zippy-multislot> <h3>使用标签锁定投影位置</h3> <div class="my-class">使用class锁定投影位置</div> <app-my-hello>使用自定义组件名称锁定投影位置</app-my-hello> <div content>使用自定义属性锁定投影位置</div> <ng-container ngProjectAs="question"> <p>内容投影酷吗?</p> <p>内容投影酷吗?</p> <p>内容投影酷吗?</p> <p>内容投影酷吗?</p> </ng-container> </app-zippy-multislot>
3. 有条件的投影
- 首先定义两个指令
// ContentDirective 用于获取 templateRef 操作元素内容 import { Directive, TemplateRef } from '@angular/core'; @Directive({ selector: '[appContent]', }) export class ContentDirective { constructor(public templateRef: TemplateRef<unknown>) {} }
// ToggleDirective 获取元素点击事件, 操作容器元素属性 import { Directive, HostListener } from '@angular/core'; import { ContainerTestComponent } from './container-test.component'; @Directive({ selector: '[appToggle]', }) export class ToggleDirective { @HostListener('click') toggle() { this.app.expanded = !this.app.expanded; } constructor(public app: ContainerTestComponent) {} }
- 定义包含 ng-container 容器组件: container-test。容器组件使用了 之前定义的 ContentDirective 指令
<!-- component template --> <div style="border: 1px solid;"> <ng-content select="[button]"></ng-content> <p *ngIf="expanded"> <ng-container [ngTemplateOutlet]="content.templateRef"> </ng-container> </p> </div>
// component ts script import { Component, OnInit, ContentChild } from '@angular/core'; import { ContentDirective } from './content.directive'; @Component({ selector: 'app-container-test', templateUrl: './container-test.component.html', styleUrls: ['./container-test.component.css'] }) export class ContainerTestComponent implements OnInit { expanded: boolean = false; @ContentChild(ContentDirective) content!: ContentDirective; constructor() { } ngOnInit(): void { } }
- 由 slot-test 组件调用容器组件,slot 用到了之前定义的 appToggle 组件,点击切换按钮,组件内会隐藏/显示 组件 app-my-hello 的内容
<!-- component template --> <app-container-test> <div button> <button appToggle>切换</button> </div> <ng-template appContent> <app-my-hello>有条件的内容投影~</app-my-hello> </ng-template> </app-container-test>
// component ts script import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-slot-test', templateUrl: './slot-test.component.html', styleUrls: ['./slot-test.component.css'] }) export class SlotTestComponent implements OnInit { constructor() { } ngOnInit(): void { } }
网友评论