最近我在搞项目的时候,碰到了一个叫做ant-design的tabs组件。它的需求是要我懒懒地加载组件,还要动态地渲染。你说这不是要求颇高吗?
用ant-design-angular的tabs来做懒加载,轻轻松松
@Component({
selector: 'nz-demo-tabs-lazy',
template: `
<nz-tabset>
<nz-tab nzTitle="Tab Eagerly 1">
<nz-demo-tab-content-eagerly></nz-demo-tab-content-eagerly>
</nz-tab>
<nz-tab nzTitle="Tab Eagerly 2">
<nz-demo-tab-content-eagerly></nz-demo-tab-content-eagerly>
</nz-tab>
<nz-tab nzTitle="Tab Lazy 1">
<ng-template nz-tab>
<nz-demo-tab-content-lazy></nz-demo-tab-content-lazy>
</ng-template>
</nz-tab>
<nz-tab nzTitle="Tab Lazy 2">
<ng-template nz-tab>
<nz-demo-tab-content-lazy></nz-demo-tab-content-lazy>
</ng-template>
</nz-tab>
</nz-tabset>
`
})
看,只需加上<ng-template nz-tab></ng-template>标签,并将需要懒加载的组件放在里面即可。
Angular动态渲染,简单粗暴
-
首先,来一个锚点组件:
@Component({
selector: 'my-anchor',
template: '<ng-template></ng-template>'
})
export class TbAnchorComponent {
constructor(public viewContainerRef: ViewContainerRef) { }
}
-
然后,把这个锚点组件放在需要动态渲染的地方:
<my-anchor #Anchor></my-anchor>
-
接着,挂载组件
// 通过ViewChildren获取这个组件
@ViewChildren("Anchor") Anchor!: QueryList<TbAnchorComponent>;
// 挂载
this.componentsMappings.children.forEach((compInfo, i) => {
this.createComponent(compInfo, this.Anchor.toArray()[i])
});
实现两者的结合
- 模板
<nz-tabset>
<ng-container *ngFor="let item of componentsMappings?.children">
<nz-tab [nzTitle]="item.mapping_name">
<ng-template nz-tab>
<my-anchor #Anchor></my-anchor>
</ng-template>
</nz-tab>
</ng-container>
</nz-tabset>
- ts
ngAfterViewInit(): void {
this.componentsMappings.children.forEach((compInfo, i) => {
this.createComponent(compInfo, this.Anchor.toArray()[i])
});
}
private createComponent(compInfo: ISmdComponentMappings, Anchor: TbAnchorComponent) {
const viewContainerRef = Anchor.viewContainerRef;
if (viewContainerRef.length <= 0) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(SmdColumnComponent);
const componentRef = viewContainerRef.createComponent(componentFactory);
const component = componentRef.instance;
component.componentsMappings = compInfo;
}
}
大家看看上面的实现方式是否有问题。其实是有问题的,没有实现懒加载+动态渲染的目标。因为在生命周期钩子ngAfterViewInit的forEach中已经全部将组件动态挂载到Anchor上了。需要实现点击tab的时候在挂载组件。所以我们可以改造这个实现方式,监听tab的点击事件,然后在相对应的tab里获取到这个Anchor。然后动态挂载这个组件。
第一步 改造模版
首先删掉<ng-template nz-tab></ng-template>标签。然后给<nz-tabset (nzSelectedIndexChange)="selectChange($event)"> 添加点击监听。
<nz-tabset (nzSelectedIndexChange)="selectChange($event)">
<ng-container *ngFor="let item of componentsMappings?.children">
<nz-tab [nzTitle]="item.mapping_name">
<my-anchor #Anchor></my-anchor>
</nz-tab>
</ng-container>
</nz-tabset>
第二步 监听点击切换的index
selectChange($event) {
this.componentsMappings.children.forEach((compInfo, i) => {
if (i == $event) {
this.createComponent(compInfo, this.Anchor.toArray()[$event])
}
});
}
// 创建组件的方法
private createComponent(compInfo: ISmdComponentMappings, Anchor: TbAnchorComponent) {
const viewContainerRef = Anchor.viewContainerRef;
if (viewContainerRef.length <= 0) {
const componentFactory = this.componentFactoryResolver.resolveComponentFactory(SmdColumnComponent);
const componentRef = viewContainerRef.createComponent(componentFactory);
const component = componentRef.instance;
component.componentsMappings = compInfo;
}
}
// 在页面初始化的时候手动给index为0的Anchor创建组件。
ngAfterViewInit(): void {
this.selectChange(0);
}
这样,你就像把钥匙插进锁眼,轻轻一转,项目就慢悠悠地懒加载和动态渲染了。不信?试试看!如有其他实现方式,欢迎讨论。