Angular使用动态加载组件方法实现Dialog的示例

网友投稿 415 2023-02-05


Angular使用动态加载组件方法实现Dialog的示例

网上的文章和教程基本上写到组件加载完成就没了!没了?!而且都是只能存在一个dialog,想要打开另一个dialog必须先销毁当前打开的dialog,之后看过 material 的实现方式,怪自己太蠢看不懂源码,就只能用自己的方式来实现一个dialog组件了

Dialog组件的目标:可以同时存在多个Dialog,可销毁指定Dialog,销毁后html中无组件残留且提供回调

动态加载组件的实现方式有两种,angular4.0版本之前使用ComponentFactoryResolver来实现,4.0之后可以使用更便捷的ngComponentOutlet来实现,

通过ComponentFactoryResolver实现动态载入

首先理一下ViewChild、ViewChildren、ElementRef、ViewContainerRef、ViewRef、ComponentRef、ComponentFactoryResolver之间的关系:

ViewChild 与 ViewChildren

ViewChild是通过模板引用变量(#)或者指令(directive)用来获取 Angular Dom 抽象类,ViewChild可以使用 ElementRef 或者 ViewContainerRef 进行封装。

@ViewChild('customerRef') customerRef:ElementRef;

ViewChildren通过模板引用变量或者指令用来获取QueryList,像是多个ViewChild组成的数组。

@ViewChildren(ChildDirective) viewChildren: QueryList;

ElementRef 与 ViewContainerRef

ViewChild可以使用 ElementRef 或者 ViewContainerRef 进行封装,那么 ElementRef 和 ViewContainerRef 的区别是什么?

用 ElementRef 进行封装,然后通过 .nativeElement 来获取原生Dom元素

console.log(this.customerRef.nativeElement.outerHTML);

ViewContainerRef :视图的容器,包含创建视图的方法和操作视图的api(组件与模板共同定义了视图)。api会返回 ComponentRef 与 ViewRef,那么这两个又是什么?

// 使用ViewContainetRef时,请使用read声明

@ViewChild('customerRef',{read: ViewContainerRef}) customerRef:ViewContainerRef;

this.customerRef.createComponent(componentFactory) // componentFactory之后会提到

ViewRef 与 ComponentRef

ViewRef 是最小的UI单元,ViewContainerRef api操作和获取的就是ViewRef

ComponentRef:宿主视图(组件实例视图)通过 ViewContainerRef 创建的对组件视图的引用,可以获取组件的信息并调用组件的方法

ComponentFactoryResolver

要获取 ComponentRef ,需要调用 ViewContainer 的 createComponent 方法,方法需要传入ComponentFactoryResolver创建的参数

constructor(

private componentFactoryResolver:ComponentFactoryResolver

) { }

viewInit(){

componentFactory =

this.componentFactoryResolver.resolveComponentFactory(DialogComponent);

// 获取对组件视图的引用,到这一步就已经完成了组件的动态加载

componentRef = this.customerRef.createComponent(componentFactory);

// 调用载入的组件的方法

componentRef.instance.dialogInit(component);

}

具体实现

let componentFactory,componentRef;

@ViewChild('customerRef',{read: ViewContainerRef}) customerRef:ViewContainerRef;

constructor(

private componentFactoryResolver:ComponentFactoryResolver

) { }

viewInit(){

// DialogComponent:你想要动态载入的组件,customerRef:动态组件存放的容器

componentFactory =

this.componentFactoryResolver.resolveComponentFactory(DialogComponent);

componentRef = this.customerRef.createComponent(componentFactory);

}

通过ngComponentOutlet实现动态载入

ngComponentOutlet 大大缩减了代码量,但是只有带4.0之后的版本才支持

具体实现

在dialog.component.html建立动态组件存放节点

将组件(不是组件名称)传入,就OK了,为什么可以这么简单!

dialogInit(component){

this.componentName = component;

};

Dialog的实现

实现的思路是这样的:首先创建一个dialog组件用来承载其他组件,为dialog创建遮罩和动画,建立一个service来控制dialog的生成和销毁,不过service只生成dialog,dialog内的组件还是需要在dialog组件内进行生成

1、首先写一个公共的service,用来获取根组件的viewContainerRef(尝试过 ApplicationRef 获取根组件的 viewContainerRef 没成功,所以就写成service了)

gerRootNode(...rootNodeViewContainerRef){

if(rootNode){

return rootNode;

}else {

rootNode = rootNodeViewContainerRef[0];

};

}

// 然后再根组件.ts内调用

this.fn.gerRootNode(this.viewcontainerRef);

2、创建dialog.service.ts,定义open、close三个方法,使用ViewContainerRef创建dialog组件,创建之前需要调用 ComponentFactoryReslover,并将DialogComponent传入

let componentFactory;

let componentRef;

@Injectable()

export class DialogService {

constructor(

private componentFactoryResolver:ComponentFactoryResolver

private fn:FnService

) { }

open(component){

componentFactory =

this.componentFactoryResolver.resolveComponentFactory(DialogComponent);

// 这里的获取的是ComponentRef

containerRef = this.fn.gerRootNode().createComponent(componentFactory);

// 将containerRef存储下来,以便之后的销毁

containerRefArray.puhttp://sh(containerRef);

// 调用了组件内的初始化方法,后面会提到

return containerRef.instance.dialogInit(component,containerRef);

}

// 这里有两种情况,一种是在当前组件和dialog组件关闭调用的,因为有返回值所以可以关闭指定的dialog;还有一种是在插入到dialog组件内的组件调用的,因为不知道父组件的信息,所以默认关闭最后一个dialog

close(_containerRef=null){

if( _containerRef ){

return _containerRef.containerRef.instance.dialogDestory();

}else{

containerRefArray.XkaxDNOVesplice(-1,1)[0].instance.dialogDestory();

}

}

}

3、dialog.component.ts,这里使用 ngComponentOutlet 来实现(ngComponentOutlet 在下面提到,这里为了偷懒,直接拿来用了)

let containerRef,dialogRef = new DialogRef();

export class DialogComponent implements OnInit {

componentName;

constructor(

private fn:FnService

) { }

dialogInit( _component, _containerRef){

this.componentName = _component;

containerRef = _containerRef;

dialogRef['containerRef'] = containerRef;

return dialogRef;

};

dialogDestory(){

let rootNode = this.fn.gerRootNode();

// 等待动画结束再移除

setTimeout(()=>{

// 这里用到了 viewContainerRef 里的indexOf 和 remove 方法

rootNode.remove(rootNode.indexOf(containerRef.hostView));

},400);

dialogRef.close();

return true;

};

}

4、这里还创建了一个 DialogRef 的类,用来处理 dialog 关闭后的回调,这样就可以使用 XX.afterClose().subscribe() 来创建回调的方法了

@Injectable()

export class DialogRef{

public afterClose$ = new Subject();

constructor(){}

close(){

this.afterClose$.next();

this.afterClose$.complete();

}

afterClose(){

return this.afterClose$.asObservable();

}

}

创建和销毁dialog

// 创建

let _viewRef = this.dialogService.open(DialogTestComponent);

_viewRef.afterClose().subscribe(()=>{

console.lhttp://og('hi');

});

// 销毁

this.dialogService.close()


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java注解处理器学习之编译时处理的注解详析
下一篇:java 线程池 状态(java查看线程池状态)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~