Angular使用動(dòng)態(tài)加載組件方法實(shí)現(xiàn)Dialog的示例
網(wǎng)上的文章和教程基本上寫(xiě)到組件加載完成就沒(méi)了!沒(méi)了?!而且都是只能存在一個(gè)dialog,想要打開(kāi)另一個(gè)dialog必須先銷毀當(dāng)前打開(kāi)的dialog,之后看過(guò) material 的實(shí)現(xiàn)方式,怪自己太蠢看不懂源碼,就只能用自己的方式來(lái)實(shí)現(xiàn)一個(gè)dialog組件了
Dialog組件的目標(biāo):可以同時(shí)存在多個(gè)Dialog,可銷毀指定Dialog,銷毀后html中無(wú)組件殘留且提供回調(diào)
動(dòng)態(tài)加載組件的實(shí)現(xiàn)方式有兩種,angular4.0版本之前使用ComponentFactoryResolver來(lái)實(shí)現(xiàn),4.0之后可以使用更便捷的ngComponentOutlet來(lái)實(shí)現(xiàn),
通過(guò)ComponentFactoryResolver實(shí)現(xiàn)動(dòng)態(tài)載入
首先理一下ViewChild、ViewChildren、ElementRef、ViewContainerRef、ViewRef、ComponentRef、ComponentFactoryResolver之間的關(guān)系:
ViewChild 與 ViewChildren
ViewChild是通過(guò)模板引用變量(#)或者指令(directive)用來(lái)獲取 Angular Dom 抽象類,ViewChild可以使用 ElementRef 或者 ViewContainerRef 進(jìn)行封裝。
@ViewChild('customerRef') customerRef:ElementRef;
ViewChildren通過(guò)模板引用變量或者指令用來(lái)獲取QueryList,像是多個(gè)ViewChild組成的數(shù)組。
@ViewChildren(ChildDirective) viewChildren: QueryList<ChildDirective>;
ElementRef 與 ViewContainerRef
ViewChild可以使用 ElementRef 或者 ViewContainerRef 進(jìn)行封裝,那么 ElementRef 和 ViewContainerRef 的區(qū)別是什么?
用 ElementRef 進(jìn)行封裝,然后通過(guò) .nativeElement 來(lái)獲取原生Dom元素
console.log(this.customerRef.nativeElement.outerHTML);
ViewContainerRef :視圖的容器,包含創(chuàng)建視圖的方法和操作視圖的api(組件與模板共同定義了視圖)。api會(huì)返回 ComponentRef 與 ViewRef,那么這兩個(gè)又是什么?
// 使用ViewContainetRef時(shí),請(qǐng)使用read聲明
@ViewChild('customerRef',{read: ViewContainerRef}) customerRef:ViewContainerRef;
···
this.customerRef.createComponent(componentFactory) // componentFactory之后會(huì)提到
ViewRef 與 ComponentRef
ViewRef 是最小的UI單元,ViewContainerRef api操作和獲取的就是ViewRef
ComponentRef:宿主視圖(組件實(shí)例視圖)通過(guò) ViewContainerRef 創(chuàng)建的對(duì)組件視圖的引用,可以獲取組件的信息并調(diào)用組件的方法
ComponentFactoryResolver
要獲取 ComponentRef ,需要調(diào)用 ViewContainer 的 createComponent 方法,方法需要傳入ComponentFactoryResolver創(chuàng)建的參數(shù)
constructor(
private componentFactoryResolver:ComponentFactoryResolver
) { }
viewInit(){
componentFactory =
this.componentFactoryResolver.resolveComponentFactory(DialogComponent);
// 獲取對(duì)組件視圖的引用,到這一步就已經(jīng)完成了組件的動(dòng)態(tài)加載
componentRef = this.customerRef.createComponent(componentFactory);
// 調(diào)用載入的組件的方法
componentRef.instance.dialogInit(component);
}
具體實(shí)現(xiàn)
let componentFactory,componentRef;
@ViewChild('customerRef',{read: ViewContainerRef}) customerRef:ViewContainerRef;
constructor(
private componentFactoryResolver:ComponentFactoryResolver
) { }
viewInit(){
// DialogComponent:你想要?jiǎng)討B(tài)載入的組件,customerRef:動(dòng)態(tài)組件存放的容器
componentFactory =
this.componentFactoryResolver.resolveComponentFactory(DialogComponent);
componentRef = this.customerRef.createComponent(componentFactory);
}
通過(guò)ngComponentOutlet實(shí)現(xiàn)動(dòng)態(tài)載入
ngComponentOutlet 大大縮減了代碼量,但是只有帶4.0之后的版本才支持
具體實(shí)現(xiàn)
在dialog.component.html建立動(dòng)態(tài)組件存放節(jié)點(diǎn)
<ng-container *ngComponentOutlet="componentName"></ng-container>
將組件(不是組件名稱)傳入,就OK了,為什么可以這么簡(jiǎn)單!
dialogInit(component){
this.componentName = component;
};
Dialog的實(shí)現(xiàn)
實(shí)現(xiàn)的思路是這樣的:首先創(chuàng)建一個(gè)dialog組件用來(lái)承載其他組件,為dialog創(chuàng)建遮罩和動(dòng)畫(huà),建立一個(gè)service來(lái)控制dialog的生成和銷毀,不過(guò)service只生成dialog,dialog內(nèi)的組件還是需要在dialog組件內(nèi)進(jìn)行生成
1、首先寫(xiě)一個(gè)公共的service,用來(lái)獲取根組件的viewContainerRef(嘗試過(guò) ApplicationRef 獲取根組件的 viewContainerRef 沒(méi)成功,所以就寫(xiě)成service了)
gerRootNode(...rootNodeViewContainerRef){
if(rootNode){
return rootNode;
}else {
rootNode = rootNodeViewContainerRef[0];
};
}
// 然后再根組件.ts內(nèi)調(diào)用
this.fn.gerRootNode(this.viewcontainerRef);
2、創(chuàng)建dialog.service.ts,定義open、close三個(gè)方法,使用ViewContainerRef創(chuàng)建dialog組件,創(chuàng)建之前需要調(diào)用 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存儲(chǔ)下來(lái),以便之后的銷毀
containerRefArray.push(containerRef);
// 調(diào)用了組件內(nèi)的初始化方法,后面會(huì)提到
return containerRef.instance.dialogInit(component,containerRef);
}
// 這里有兩種情況,一種是在當(dāng)前組件和dialog組件關(guān)閉調(diào)用的,因?yàn)橛蟹祷刂邓钥梢躁P(guān)閉指定的dialog;還有一種是在插入到dialog組件內(nèi)的組件調(diào)用的,因?yàn)椴恢栏附M件的信息,所以默認(rèn)關(guān)閉最后一個(gè)dialog
close(_containerRef=null){
if( _containerRef ){
return _containerRef.containerRef.instance.dialogDestory();
}else{
containerRefArray.splice(-1,1)[0].instance.dialogDestory();
}
}
}
3、dialog.component.ts,這里使用 ngComponentOutlet 來(lái)實(shí)現(xiàn)(ngComponentOutlet 在下面提到,這里為了偷懶,直接拿來(lái)用了)
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();
// 等待動(dòng)畫(huà)結(jié)束再移除
setTimeout(()=>{
// 這里用到了 viewContainerRef 里的indexOf 和 remove 方法
rootNode.remove(rootNode.indexOf(containerRef.hostView));
},400);
dialogRef.close();
return true;
};
}
4、這里還創(chuàng)建了一個(gè) DialogRef 的類,用來(lái)處理 dialog 關(guān)閉后的回調(diào),這樣就可以使用 XX.afterClose().subscribe() 來(lái)創(chuàng)建回調(diào)的方法了
@Injectable()
export class DialogRef{
public afterClose$ = new Subject();
constructor(){}
close(){
this.afterClose$.next();
this.afterClose$.complete();
}
afterClose(){
return this.afterClose$.asObservable();
}
}
創(chuàng)建和銷毀dialog
// 創(chuàng)建
let _viewRef = this.dialogService.open(DialogTestComponent);
_viewRef.afterClose().subscribe(()=>{
console.log('hi');
});
// 銷毀
this.dialogService.close()
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解AngularJS如何實(shí)現(xiàn)跨域請(qǐng)求
跨域請(qǐng)求一直是網(wǎng)頁(yè)編程中的一個(gè)難題,在過(guò)去,絕大多數(shù)人都傾向于使用JSONP來(lái)解決這一問(wèn)題。不過(guò)現(xiàn)在,我們可以考慮一下W3C中一項(xiàng)新的特性——CORS(Cross-Origin Resource Sharing)了。2016-08-08
Angularjs實(shí)現(xiàn)多圖片上傳預(yù)覽功能
這篇文章主要介紹了Angularjs實(shí)現(xiàn)多圖片上傳預(yù)覽功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
AngularJS 雙向數(shù)據(jù)綁定詳解簡(jiǎn)單實(shí)例
這篇文章主要介紹了AngularJS 雙向數(shù)據(jù)綁定詳解簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2016-10-10
詳解Angular的內(nèi)置過(guò)濾器和自定義過(guò)濾器【推薦】
在實(shí)際的開(kāi)發(fā)過(guò)程中,很多后端返回給我們的數(shù)據(jù)都是需要格式化處理的,在angular中為我們內(nèi)置提供了filter指令,可以很方便的對(duì)數(shù)據(jù)進(jìn)行處理。本文將對(duì)Angular的內(nèi)置過(guò)濾器和自定義過(guò)濾器進(jìn)行詳細(xì)介紹,下面跟著小編一起來(lái)看下吧2016-12-12
angularjs實(shí)現(xiàn)與服務(wù)器交互分享
AngularJS是Google開(kāi)發(fā)的純客戶端JavaScript技術(shù)的WEB框架,用于擴(kuò)展、增強(qiáng)HTML功能,它專為構(gòu)建強(qiáng)大的WEB應(yīng)用而設(shè)計(jì)。2014-06-06

