博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Angular4 动态加载组件杂谈
阅读量:6427 次
发布时间:2019-06-23

本文共 3889 字,大约阅读时间需要 12 分钟。

最近接手了一个项目,客户提出了一个高大上的需求:要求只有一个主界面,所有组件通过Tab来显示。其实这个需求并不诡异,不喜欢界面跳转的客户都非常热衷于这种展现形式。

好吧,客户至上,搞定它!这种实现方式在传统的HTML应用中,非常简单,只是在这Angular4(以下简称ng)中,咋个弄呢?

我们先来了解下ng中动态加载组件的两种方式:

  1. 加载已经声明的组件: 使用,将一个组件实例呈现到另一个组件视图上;
  2. 动态创建组件并加载:使用ComponentFactory和Compiler,创建和呈现组件

根据我们的需求,各个组件是事先开发好的,需要在同一个组件上显示出来。所以第一种方式符合我们的要求。

使用ComponentFactoryResolver动态加载组件,需要先了解如下概念:

  1. ViewChild:属性装饰器,通过它可以获得视图上对应的元素;
  2. ViewContainerRef:视图容器,可在其上创建、删除组件;
  3. ComponentFactoryResolver:组件解析器,可以将一个组件呈现在另一个组件的视图上。

搞明白了概念,看看代码吧:

HTML代码
ts代码import {Component, Input, ViewContainerRef, ViewChild, ComponentFactoryResolver,ComponentRef,OnDestroy,OnInit} from '@angular/core';import {RoleComponent} from "./role/role.component";@Component({   selector: 'dynamic-container',   entryComponents: [RoleComponent,....],  //需要动态加载的组件名,这里一定要指定,否则报错   template: "
"})export class DynamicComponent implements OnDestroy,OnInit { @ViewChild('container', { read: ViewContainerRef }) container: ViewContainerRef; @Input() componentName //需要加载的组件名 compRef: ComponentRef
; // 加载的组件实例 constructor(private resolver: ComponentFactoryResolver) {} loadComponent() { let factory = this.resolver.resolveComponentFactory(this.componentName); if (this.compRef) { this.compRef.destroy(); } this.compRef = this.container.createComponent(factory) //创建组件 } ngAfterContentInit() { this.loadComponent() } ngOnDestroy() { if(this.compRef){ this.compRef.destroy(); } }}

代码的确不复杂!

可是,如果加载的组件有传入的参数,比如修改角色组件,需要传入角色id,该怎么办呢?有办法解决,使用ReflectiveInjector(依赖注入),在加载组件时将需要传入的参数注入到组件中。代码调整如下:

HTML代码,增加了inputs参数,其值为参数值对
ts代码import { ReflectiveInjector} from '@angular/core';......export class DynamicComponent implements OnDestroy,OnInit {        @Input() inputs:any         //加载组件需要传入的参数组    .......    loadComponent() {       let factory = this.resolver.resolveComponentFactory(this.componentName);            if(!this.inputs)          this.inputs={}          let inputProviders = Object.keys(this.inputs).map((inputName) => {           return {provide: inputName, useValue: this.inputs[inputName]};});           let resolvedInputs = ReflectiveInjector.resolve(inputProviders);               let injector = ReflectiveInjector.fromResolvedProviders(resolvedInputs, this.container.parentInjector);           if (this.compRef) {         this.compRef.destroy();        }       this.compRef = factory.create(injector) //创建带参数的组件       this.container.insert(this.compRef.hostView);//呈现组件的视图  }  ngAfterContentInit() {    this.loadComponent()  }  ......}RoleComponent代码如下export class RoleComponent implements OnInit {    myName:string    ........    constructor(){        //this.myName的值为dynamic   }}

到此,动态加载组件的界面骄傲滴显示在界面上。等等,貌似哪里不对!为什么界面上从后台获取的数据没有加载?

获取数据的代码如下:

export class RoleComponent implements OnInit {   roleList=[];   ......   constructor(private _roleService.list:RoleService) {      this._roleService.list().subscribe(res=>{          this.roleList=res.roleList;      });   }   ......}

经过反复测试,得出结论如下:从后台通过HTTP获取的数据已经获得,只是没有触发ng进行变更检测,所以界面没有渲染出数据。

抱着“遇坑填坑”的信念,研习ng的文档,发现ng支持手动触发变更检测,只要在适当的位置调用变更检测即可。同时,ng提供了不同级别的变更检测:

  1. 变更检测策略:

    Default :ng提供的Default的检测策略,只要组件的input发生改变,就触发检测;  OnPush :OnPush检测策略是input发生改变,并不立即触发检测,而是输入的引用发生变化时,才会触发检测。
  2. ChangeDetectorRef.detectChanges():可显式的控制变更检测,在需要的地方使用即可;
  3. NgZone.run():在整个应用中进行变更检测
  4. ApplicationRef.tick():在整个应用中进行变更检测,侦听NgZone的onTurnDone事件,来触发检测

根据文档显示,ng应用缺省就在使用NgZone来检测变更,这对于正常加载的组件是没有问题的,但是对于动态加载的组件却不起作用。几次试验下来,唯有第二种方法起作用:显式调用ChangeDetectorRef.detectChanges()

于是修改ts代码:

interval:any loadComponent() {    ......    this.interval=setInterval(() => {      this.compRef.changeDetectorRef.detectChanges();    }, 50);  //50毫秒检测一次变更}ngOnDestroy() {    ......    clearInterval(this.interval)}

鉴于本人的ng技能尚浅,就用这种笨拙的方法解决了数据加载问题,但是如鲠在喉,总觉应该还有更优雅的解决方法,待我再花时日研究下。

啰嗦至此,文中如有不妥之处,欢迎各位看官指正。

补充一句,强烈推荐,它提供了丰富的前端组件,可以方便取用,大大节省了界面的开发速度。

参考文献:

转载地址:http://ccfga.baihongyu.com/

你可能感兴趣的文章
ref和out
查看>>
黑客教父详解账号泄露全过程:1亿用户已泄露
查看>>
程序员必须软件
查看>>
Canvas里的globalCompositeOperation
查看>>
解决Unable to locate theme engine in module_path: "pixmap"
查看>>
贝叶斯文本分类c#版
查看>>
Centos安装KDE或GNOME
查看>>
Eclipse & IDEA 中常用的快捷键
查看>>
javascript ---IPhone滑动解锁
查看>>
table固定行和表头
查看>>
<每天读一点职场心理学>读书笔记
查看>>
Android权限大全代码
查看>>
android 判断SIM卡是哪个运营商
查看>>
删除N天前的M(天)个目录 、删除N天前最后修改的文件 ForFiles, dos command 批处理命令cmd/bat...
查看>>
十进制数1~n中1出现的次数
查看>>
PostgreSQL 的 语法分析的理解(五)
查看>>
[转载]Visual Studio 2010敏捷利剑:详解Scrum
查看>>
Java Collection: List、Set、 Map、 HashMap、 Hashtable、 Vector
查看>>
T-SQL查询进阶--流程控制语句
查看>>
Excel VBA小试
查看>>