首頁 >web前端 >js教程 >怎麼操作Angular使用動態載入元件方法實作Dialog

怎麼操作Angular使用動態載入元件方法實作Dialog

php中世界最好的语言
php中世界最好的语言原創
2018-05-30 11:32:001825瀏覽

這次帶給大家怎樣操作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中文网其它相关文章!

推荐阅读:

怎样操作Node.js 使用jade模板引擎

Node.js Express安装与使用详细介绍

以上是怎麼操作Angular使用動態載入元件方法實作Dialog的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn