angular 14 替换 ComponentFactoryResolver 实现动态创建组件
需求
移植弹出框到 angular
项目中。
方案
直接使用其他项目依赖
比较常用的有以下两个
但是通过查看源码,发现都使用使用 ComponentFactoryResolver
来实现组件的新建,然后通过document.body.appendChild
添加到页面上的,
但是,angular 文档中提示,ComponentFactoryResolver
不推荐使用。
Deprecated: Angular no longer requires Component factories. Please use other APIs where Component class can be used directly.
Note: since v13, dynamic component creation via ViewContainerRef.createComponent does not require resolving component factory: component class can be used directly.
自己实现
没有了 ComponentFactoryResolver
, 那么就使用 ViewContainerRef.createComponent
主要思路:
-
新建一个容器组件,获取到
ViewContainerRef
-
新建一个全局的服务提供者,
interface IDialogRef {
id: any;
element: ComponentRef<any>;
}
@Injectable({
providedIn: 'root'
})
export class DialogService {
private dialogItems: IDialogRef[] = [];
public containerRef: ViewContainerRef;
constructor(
private injector: Injector,
) {
}
/**
* 加载loading
* @param option
* @returns loading 的id, 使用 remove(id: any) 进行关闭
*/
public loading(option?: DialogLoadingOption): any {
option = Object.assign({}, option, {
time: 2000,
closeable: true,
});
return this.createDailog(DialogLoadingComponent, option);
}
/**
* 创建组件
* @param component
* @param option
* @returns
*/
private createDailog<T>(component: Type<T>, option: any): any {
const dialogId = ++ DialogService.guid;
if (!this.containerRef) {
return;
}
const dialogInjector = new DialogInjector(new DialogPackage(option, dialogId), this.injector);
const dialogRef = this.containerRef.createComponent(component, {
injector: dialogInjector
});
this.dialogItems.push({
id: dialogId,
element: dialogRef
});
return dialogId;
}
/**
* 删除组件
* @param i
*/
private removeAt(i: number) {
const item = this.dialogItems[i];
this.dialogItems.splice(i, 1);
const dialogRef = item.element;
dialogRef.destroy();
}
}
作为对比,ViewContainerRef.createComponent
比 ComponentFactoryResolver
使用更简单,而且更符合 angular
特色
constructor(
private resolver: ComponentFactoryResolver,
private applicationRef: ApplicationRef,
private injector: Injector,
@Inject(DOCUMENT) private document: Document,
) { }
// 添加组件
const dialogFactory = this.resolver.resolveComponentFactory(component);
const dialogRef = dialogFactory.create(dialogInjector);
this.applicationRef.attachView(dialogRef.hostView);
this.document.body.appendChild(dialogRef.location.nativeElement);
// 删除组件
this.applicationRef.detachView(dialogRef.hostView);
this.document.body.removeChild(dialogRef.location.nativeElement);
this.dialogItems.splice(i);
- 在组件内部实现删除功能
export class DialogMessageComponent implements OnDestroy {
constructor(
private data: DialogPackage<DialogMessageOption>,
private service: DialogService,
) {
}
@HostListener('click')
public close() {
this.service.remove(this.data.dialogId);
}
}
- 以模块的方式提供
@NgModule({
imports: [
CommonModule,
],
declarations: [
...COMPONENTS
],
exports: [
...COMPONENTS
],
})
export class DialogModule {
static forRoot(): ModuleWithProviders<DialogModule> {
return {
ngModule: DialogModule,
providers: [
DialogService
]
};
}
}
- 使用
第一步,在 app.module.ts
中导入
第二步,在 app.component.ts
页面中添加容器
第三步,使用
总结
ViewContainerRef.createComponent
比 ComponentFactoryResolver
使用更简单,而且更符合 angular
特色,始终保持所有创建的内容在框架内。
完整代码请查看 Angular-ZoDream
转载请保留原文链接: https://zodream.cn/blog/id/231.html