Skip to content

起因

项目中使用ng-zorro Antd的Description进行一个列表的展示,现在需要为其中某几个字段添加隐藏展示功能。
隐藏时使用***展示 点击后进行相关验证后再展示真实数据。

方案一 在组件中添加n个变量 来控制对应字段的隐藏和展示。=> 可扩展性极差

方案二<nz-description-item>的基础上进行封装,生成<private-description-item>组件,内部维护一套状态 => 良好的解决问题

遇到问题

<nz-description>组件中添加自己的<private-description-item>组件,不会显示。 原因分析:在nz-description组件中, Antd使用了@ContentChildren来接收要渲染的子组件。如下图: code

@ContentChildren(NzDescriptionsItemComponent)指定了可以被渲染的子组件仅限于NzDescriptionsItemComponent类型
不接受其他任何组件,本质上@ContentChildren是从内容投影中选择了一部分要渲染的内容。

那是不是就没有办法了呢?经过我的查找,找到了对应的解决方法。如下代码所示

typescript
import { Component, forwardRef, Input, OnInit } from '@angular/core';
import { NzDescriptionsItemComponent } from 'ng-zorro-antd/descriptions';

@Component({
  selector: 'app-private-description-item',
  template: `
    <ng-template>
         // 供父组件读取的内容
         <ng-content *ngIf='visible'></ng-content>
         // 此处可以自由定义需要的样式
         <div *ngIf='!visible' (click)='visible = true'>*******</div>
    </ng-template> 
  `,
  styleUrls: ['./private-description-item.component.scss'],
  providers: [
      {
          provide: NzDescriptionsItemComponent,
          useExisting: forwardRef(() => PrivateDescriptionItemComponent)
      }
  ]
})
export class PrivateDescriptionItemComponent extends NzDescriptionsItemComponent implements OnInit {

  @Input() title: any;

  visible = false; // 控制展示与隐藏

  @Input() nzTitle: string;

  @Input() nzSpan: number;

  constructor() {
      super();
  }

  ngOnInit(): void {
  }
}

核心点:

  1. 组件继承NzDescriptionsItemComponent,包含需要被父组件读取的属性nzTitle, nzSpan
  2. 指定providers属性,其中对外暴露NzDescriptionsItemComponent, 并指定Angular使用已存在的值: useExist(forwardRef(() => PrivateDescriptionItemComponent))对外转发当前组件。

useClass与useExist的区别

先看一张来自stackOverFlow的图片: di

四种类型的provider本质上都是经过一个工厂函数(factory)转化而来,useClass从参数中解析依赖然后创建一个新的类实例, 而useExist返回目前已有的类实例(可以看作是创建了一个引用)

useExist中的forwardRef

typescript
// Angular 源码
export function resolveForwardRef(type: any): any {
  if (typeof type === 'function' && type.hasOwnProperty('__forward_ref__') &&
      type.__forward_ref__ === forwardRef) {
    return (<ForwardRefFn>type)();
  } else {
    return type;
  }
}

使用forwardRef包裹后,允许引用指向一个尚未被定义的实例(在本例中就是PrivateDescriptionItemComponent组件,其尚未被实例化)

For instance, forwardRef is used when the token which we need to refer to for the purposes of DI is declared, but not yet defined. It is also used when the token which we use when creating a query is not yet defined

实现原理:闭包

参考文章

  1. stackOverFlow
  2. 官方文档
  3. Angular Dependency Injection
  4. indepth.dev
  5. offering.solutions