当前位置: 移动技术网 > IT编程>脚本编程>AngularJs > Angular使用动态加载组件方法实现Dialog的示例

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

2018年05月19日  | 移动技术网IT编程  | 我要评论

网上的文章和教程基本上写到组件加载完成就没了!没了?!而且都是只能存在一个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()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网