在使用一个组件的时候。可以通过在组件的元数据的selector中设置的标签来使用。也可以动态的创建一个组件来使用。
通过在组件的元数据的 selector 中设置的标签来使用组件
以可编辑的table(EditTableComponent) 这个组件为例,这个组件的元数据中selector 设置的标签为:“app-edit-table”;
所以在其他组件中使用这个组件的时候,只需要在组件的模版文件中的模版文件中通过
<app-edit-table></app-edit-table>
的方式就可以在父组件中创建这个子组件了。
在父组件中动态的创建组件
在动态创建组件的时候,主要用到了Angular中的ViewContainerRef 和 ComponentFactoryResolver 这两个类中的API来实现。
创建一个父组件,在这个父组件中动态创建子组件
使用命令
ng g c dynamic-com
来创建组件
wujiayudeMacBook-Pro:pages wjy$ ng g c dynamic-com
CREATE src/app/pages/dynamic-com/dynamic-com.component.less (0 bytes)
CREATE src/app/pages/dynamic-com/dynamic-com.component.html (30 bytes)
CREATE src/app/pages/dynamic-com/dynamic-com.component.spec.ts (657 bytes)
CREATE src/app/pages/dynamic-com/dynamic-com.component.ts (289 bytes)
UPDATE src/app/pages/pages.module.ts (2471 bytes)
在组件 dynamic-com 中注入 ViewContainerRef
修改组件的模版文件:
<ng-template #dynamic></ng-template>
由于 元素不会渲染出任何额外的输出,所以它是动态加载组件的最佳选择。
修改组件的类文件:
import {
AfterContentInit,
Component,
ComponentFactoryResolver,
ComponentRef,
OnInit,
ViewChild,
ViewContainerRef
} from '@angular/core';
@Component({
selector: 'app-dynamic-com',
templateUrl: './dynamic-com.component.html',
styleUrls: ['./dynamic-com.component.less']
})
export class DynamicComComponent implements OnInit, AfterContentInit {
@ViewChild('dynamic', {read: ViewContainerRef}) dynamicCom: ViewContainerRef;
constructor() {
}
ngOnInit() {
}
ngAfterContentInit() {
}
}
组件注入了 ViewContainerRef 来获取对容器视图的访问权,这个容器就是那些动态加入的组件的宿主,在这里,动态创建的组件 “EditTableComponent” 就会以 dynamic 为容器,将动态创建的 “EditTableComponent” 的组件加入到其中。
创建两个 editTable 组件的变量;
修改组件的类文件
import {
AfterContentInit,
Component,
ComponentFactoryResolver,
ComponentRef,
OnInit,
ViewChild,
ViewContainerRef
} from '@angular/core';
import {EditTableComponent} from '../edit-table/edit-table.component';
@Component({
selector: 'app-dynamic-com',
templateUrl: './dynamic-com.component.html',
styleUrls: ['./dynamic-com.component.less']
})
export class DynamicComComponent implements OnInit, AfterContentInit {
@ViewChild('dynamic', {read: ViewContainerRef}) dynamicCom: ViewContainerRef;
public comOne: ComponentRef<EditTableComponent>;
public comTwo: ComponentRef<EditTableComponent>;
constructor(private comFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit() {
}
ngAfterContentInit() {
}
}
创建组件
修改类文件
import {
AfterContentInit,
Component,
ComponentFactoryResolver,
ComponentRef,
OnInit,
ViewChild,
ViewContainerRef
} from '@angular/core';
import {EditTableComponent} from '../edit-table/edit-table.component';
@Component({
selector: 'app-dynamic-com',
templateUrl: './dynamic-com.component.html',
styleUrls: ['./dynamic-com.component.less']
})
export class DynamicComComponent implements OnInit, AfterContentInit {
@ViewChild('dynamic', {read: ViewContainerRef}) dynamicCom: ViewContainerRef;
public comOne: ComponentRef<EditTableComponent>;
public comTwo: ComponentRef<EditTableComponent>;
constructor(private comFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit() {
}
ngAfterContentInit() {
const childComp = this.comFactoryResolver.resolveComponentFactory(EditTableComponent);
this.comOne = this.dynamicCom.createComponent(childComp);
this.comOne.instance.dataSet = [{
key: '999',
name: '动态创建组件',
status: 'edit',
age: 32,
address: '来自动态的数据'
}];
this.comOne.instance.backInfo.subscribe((info) => {
console.log(info);
});
this.comTwo = this.dynamicCom.createComponent(childComp);
this.comTwo.instance.dataSet = [{
key: '1000',
name: '动态创建组件2',
status: 'edit',
age: 32,
address: '来自动态的数据2'
}];
}
}
这里通过了 ComponentFactoryResolver 的实例(comFactoryResolver)调用 resolveComponentFactory() 为 EditTableComponent 组件解析出一个 ComponentFactory,然后ComponentFactory 会为组件EditTableComponent创建一个实例。再将ViewContainerRef指向创建的实例,通过调用 ViewContainerRef 的 createComponent()将这个组件添加到模板中。对于创建出来的 comOne 这个组件,可以通过 comOne.instance属性直接访问comOne的 public 型属性,也可以通过 subscribe() 的箭头函数来订阅 comOne 的事件。
最终的效果如下:
销毁组件
通过代码动态创建出来的组件,可以手动去销毁。只要调用 comOne.destory() 就可以了。
修改模版文件:
<ng-template #dynamic></ng-template>
<button nz-button (click)="handleDestroyCom()">销毁组件</button>
实现handleDestroyCom 方法,修改类文件
import {
AfterContentInit,
Component,
ComponentFactoryResolver,
ComponentRef,
OnInit,
ViewChild,
ViewContainerRef
} from '@angular/core';
import {EditTableComponent} from '../edit-table/edit-table.component';
@Component({
selector: 'app-dynamic-com',
templateUrl: './dynamic-com.component.html',
styleUrls: ['./dynamic-com.component.less']
})
export class DynamicComComponent implements OnInit, AfterContentInit {
@ViewChild('dynamic', {read: ViewContainerRef}) dynamicCom: ViewContainerRef;
public comOne: ComponentRef<EditTableComponent>;
public comTwo: ComponentRef<EditTableComponent>;
constructor(private comFactoryResolver: ComponentFactoryResolver) {
}
ngOnInit() {
}
ngAfterContentInit() {
const childComp = this.comFactoryResolver.resolveComponentFactory(EditTableComponent);
this.comOne = this.dynamicCom.createComponent(childComp);
this.comOne.instance.dataSet = [{
key: '999',
name: '动态创建组件',
status: 'edit',
age: 32,
address: '来自动态的数据'
}];
this.comOne.instance.backInfo.subscribe((info) => {
console.log(info);
});
this.comTwo = this.dynamicCom.createComponent(childComp);
this.comTwo.instance.dataSet = [{
key: '1000',
name: '动态创建组件2',
status: 'edit',
age: 32,
address: '来自动态的数据2'
}];
}
// 销毁组件按钮回调函数
handleDestroyCom() {
this.comOne.destroy();
}
}
实现的效果如下:
渲染组件的顺序问题
在 createComponent() 中是可以有第二个参数的,第二个参数是用来控制组件渲染的顺序的。
这个顺序的索引是从 0 开始的,表示新组件的宿主视图要插入到当前容器的哪个位置。 如果没有指定,就把新的视图追加到最后。
上述代码以上传到 GitHub 的DynamicComComponent 组件中可以看到