网上的文章和教程基本上写到组件加载完成就没了!没了?!而且都是只能存在一个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<ChildDirective>;
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建立动态组件存放节点
<ng-container *ngComponentOutlet="componentName"></ng-container>
将组件(不是组件名称)传入,就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.push(containerRef);
// 调用了组件内的初始化方法,后面会提到
return containerRef.instance.dialogInit(component,containerRef);
}
// 这里有两种情况,一种是在当前组件和dialog组件关闭调用的,因为有返回值所以可以关闭指定的dialog;还有一种是在插入到dialog组件内的组件调用的,因为不知道父组件的信息,所以默认关闭最后一个dialog
close(_containerRef=null){
if( _containerRef ){
return _containerRef.containerRef.instance.dialogDestory();
}else{
containerRefArray.splice(-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.log('hi');
});
// 销毁
this.dialogService.close()
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新动态
- 小骆驼-《草原狼2(蓝光CD)》[原抓WAV+CUE]
- 群星《欢迎来到我身边 电影原声专辑》[320K/MP3][105.02MB]
- 群星《欢迎来到我身边 电影原声专辑》[FLAC/分轨][480.9MB]
- 雷婷《梦里蓝天HQⅡ》 2023头版限量编号低速原抓[WAV+CUE][463M]
- 群星《2024好听新歌42》AI调整音效【WAV分轨】
- 王思雨-《思念陪着鸿雁飞》WAV
- 王思雨《喜马拉雅HQ》头版限量编号[WAV+CUE]
- 李健《无时无刻》[WAV+CUE][590M]
- 陈奕迅《酝酿》[WAV分轨][502M]
- 卓依婷《化蝶》2CD[WAV+CUE][1.1G]
- 群星《吉他王(黑胶CD)》[WAV+CUE]
- 齐秦《穿乐(穿越)》[WAV+CUE]
- 发烧珍品《数位CD音响测试-动向效果(九)》【WAV+CUE】
- 邝美云《邝美云精装歌集》[DSF][1.6G]
- 吕方《爱一回伤一回》[WAV+CUE][454M]