共计 1949 个字符,预计需要花费 5 分钟才能阅读完成。
最近前端框架转移到蚂蚁的 Ng-Alain 和 Ng-Zorro 上,需要把我们自己的一些前端数据逻辑对接到 Ng-Zorro 提供的组件上,以简化前端代码。以前的做法是定义一组自己的自定义组件来包装 Ng-Zorro 提供的组件,但这个方法的缺点是页面上很难直接使用 Ng-Zorro 组件的属性、事件和其他特性,除非自定义组件把 Ng-Zorro 组件的属性和事件都暴露出来,Ng-Zorro 组件的属性事件非常多,全部暴露出来太麻烦,不太现实。
突然想到是否能够利用 Angular 自定义指令控制 Ng-Zorro 组件,这样页面上放置的不是自定义组件,而是 Ng-Zorro 组件,然后附加上一个自己写的自定义指令,添加自己的一些属性和事件,页面上仍然可以使用所有的 Ng-Zorro 组件的属性和事件。
网上找到的 Angular 自定义属性指令的例子大多都是注入 ElementRef 和 Renderer2,控制 html 标签的属性。没找到自定义指令控制第三方组件的例子。最先想到的是通过 @Input 方式传入 Ng-Zorro 组件的实例。如下,自定义属性指令 TreeSelectDirective 控制 Ng-Zorro 的下拉树组件 NzTreeSelectComponent
自定义指令的 ts 文件:
@Directive({
selector: ‘[myTreeSelect]’,
exportAs:’myTreeSelect’
})
export class TreeSelectDirective implements OnChanges,OnDestroy,OnInit,AfterViewInit {
@Input(“myTreeSelect”)
container:NzTreeSelectComponent=null;
@Input(“myProp”)
prop:string=”;
@Output(“ev”)
e:EventEmitter<any>=new EventEmitter();
}
页面的 HTML 模板:
<nz-tree-select #t [myTreeSelect]=’t’ [myProp]=’x’ (ev)=’onEv($event)’ …
这个方法可以在指令类 TreeSelectDirective 获得组件 nz-tree-select 的实例,并且控制 nz-tree-select 组件,但因为组件实例是在 @Input 中传入,而 @Input 是在生命周期的 ngOnInit 钩子被调用时才传入,而组件的 ngOnInit 钩子比指令的 ngOnInit 钩子先被调用,这意味着指令获得组件的实例之前,组件实例的各个输入属性已经完成初始化。这会带来一些问题,Ng-Zorro 组件的有些属性初始化之后再设置就无法生效(可能是 Zorro 的 bug),必须在 Ng-zorro 组件 ngOnInit 钩子调用前设置这些属性值才能生效,所以通过 @Input 传入组件实例,无法设置这些属性的值并使其生效。
另外,<nz-tree-select #t [myTreeSelect]=’t’ 这样的写法也太累赘,不优雅。后来发现,nz-tree-select 之类的组件也支持 Angular 官方 ngModel 指令,于是查看 ngModel 指令如何和 nz-tree-select 组件交互,发现 ngModel 指令构造器中注入了 ControlValueAccessor 实例。
然后猜想,既然指令可以注入 ControlValueAccessor,是不是也可以直接注入 NzTreeSelectComponent,于是试验:
@Directive({
selector: ‘[myTreeSelect]’,
exportAs:’myTreeSelect’
})
export class TreeSelectDirective implements OnChanges,OnDestroy,OnInit,AfterViewInit {
@Input(“myProp”)
prop:string=”;
@Output(“ev”)
e:EventEmitter<any>=new EventEmitter();
constructor(private container:NzTreeSelectComponent){
// console.info(nzComp);
}
}
页面的 HTML 模板:
<nz-tree-select myTreeSelect [myProp]=’x’ (ev)=’onEv($event)’ …
测试发现 nz-tree-select 成功注入指令实例,constructor 被调用的时候,nz-tree-select 组件的 @Input 输入属性还没有初始化,可以在 constructor 中设置 nz-tree-select 组件的属性。而且代码也更优雅简洁。