這次帶給大家怎樣操作Angular使用動態載入元件方法實作Dialog,操作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<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 與ComponentRefViewRef 是最小的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); }
let componentFactory,componentRef;
<ng-container *ngComponentOutlet="componentName"></ng-container>
let componentFactory,componentRef;dialogInit(component){
this.componentName = component;
};
let componentFactory,componentRef;
gerRootNode(...rootNodeViewContainerRef){ if(rootNode){ return rootNode; }else { rootNode = rootNodeViewContainerRef[0]; }; } // 然后再根组件.ts内调用 this.fn.gerRootNode(this.viewcontainerRef);
透過ngComponentOutlet實現動態載入
ngComponentOutlet 大幅縮減了程式碼量,但是只有帶4.0之後的版本才支援
######具體實現#########在dialog.component.html建立動態元件存放節點###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(); } } }####將元件(不是元件名稱)傳入,就OK了,為什麼可以這麼簡單! ###
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; }; }#########Dialog的實作#############實作的想法是這樣的:先建立一個dialog元件用來承載其他元件,為dialog建立遮罩和動畫,建立一個service來控制dialog的生成和銷毀,不過service只生成dialog,dialog內的元件還是需要在dialog元件內進行生成######1、先寫一個公共的service,用來取得根元件的viewContainerRef(嘗試過ApplicationRef 取得根元件的viewContainerRef 沒成功,所以就寫成service了)###
@Injectable() export class DialogRef{ public afterClose$ = new Subject(); constructor(){} close(){ this.afterClose$.next(); this.afterClose$.complete(); } afterClose(){ return this.afterClose$.asObservable(); } }###2、建立dialog.service.ts,定義open、close三個方法,使用ViewContainerRef建立dialog元件,在建立之前需要呼叫ComponentFactoryReslover,並將DialogComponent傳入###
// 创建 let _viewRef = this.dialogService.open(DialogTestComponent); _viewRef.afterClose().subscribe(()=>{ console.log('hi'); }); // 销毁 this.dialogService.close()###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()
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上是怎麼操作Angular使用動態載入元件方法實作Dialog的詳細內容。更多資訊請關注PHP中文網其他相關文章!