angular4 动态创建组件 vs 动态创建模板

???? 实现

模拟场景:页面上"帮助"按钮的点击触发帮助文档的弹出框,且每个页面的帮助文档不一样

因此弹出框里的帮助文档是一个动态模板而不是动态组件

以下comp均代表Type类型的动态组件,即 comp:Type<any>

//xx.component.ts

export class xxComponent implements Oninit{
   constructor(private helpingService:HelpingService){ }
    fireClick($event:any){//按钮的点击事件
       this.helpingService.changeHelpContent(comp);
    }
}
//helping.service.ts
@Injectable()
export class HelpingService{ helpChangeEvent:EventEmitter<comp>; constractor(){ this.helpChangeEvent=new EventEmitter<comp>() } public changeHelpContent(comp){ this.helpChangeEvent.emit(comp)//发射helpChangeEvent事件 }
}
//helping.component.ts

@component({
   selector: 'helping',
   templateUrl: './helping.component.html',
   styleUrls: ['./helping.component.scss']
})

 @ViewChild('con',{read:ViewContainerRef})
  conRef:ViewContainerRef; 

export class HelpingComponent implements OnInit{
   constractor(private helpingService:HelpingService,
               private factoryResolver: ComponentFactoryResolver,){
        this.helpingService.helpchangeEvent.subscribe((comp)=>{//接收helpChangeEvent事件
               this.loadComponent(comp)     
       })
   }
private loadComponent(comp){
     this.conRef.clear();//删除之前的视图
     let factory=this.factoryResolver.resolveComponentFactory(comp);//factory创建组件的实例
     this.conRef.createComponent(factory)   }
}

✨ 动态创建组件的相关知识点

1.TemplateRef

通过TemplateRef实例,可以创建内嵌视图Embedded Views

涉及对象和相关方法:

1.crateEbeddedView创建内嵌视图

@Component({
 selector:'helping',
 template:`//创建组件,那组件内容就是固定的
   <template #tpl>
     <span>i am a span in Template {{name}}</span>
   </template>  
   `
})
export class helpingComponent{
  @ViewChild('tpl')
  tpl:TemplateRef<any>;

  name:string='Artimis';

 ngAfterViewInit(){ 
     let view=this.tpl.createEmbeddedView(null);
     let commentEle=this.tpl.elementRef.nativeElement; 
     view.rootNodes.forEach((node)=>{//没有ViewContainerRef,只能手动把元素塞到视图里
         commentEle.parentNode.insertBefore(node,commentEle.nextSibling)
     })
 }
}

2.VIiewContainerRef

视图容器,是管理创建好的内嵌视图或组件视图

通过ViewContainerRef实例,基于TemplateRef + createEmbeddedView创建内嵌视图

@Component({
 selector:'helping',
template:`
   <template #tpl>
     <span>i am a span in Template {{name}}</span>
   </template>  
   `
})
export class helpingComponent{
  @ViewChild('tpl')
  tpl:TemplateRef<any>;  @ViewChild('tpl',{read:ViewContainerRef})
  tplVcRef:ViewContainerRef;  name:string='Artimis';

 ngAfterViewInit(){
     //有ViewContainerRef,直接通过createEmbeddedView方法塞进视图
     this.tplVcRef.createEmbeddedView(this.tplRef)
}

动态创建模板的相关知识点

1.ComponentFactoryResolver

通过ViewContainerRef实例,基于ComponentFactoryResolver + createComponent 创建模板视图

涉及对象和相关方法:

1. ComponentFactoryResolver:一个服务对象(需要注入)

2. resolveComponentFactory:ComponentFactoryResolver的方法,接收一个组件类作为参数,返回一个ComponentFactory实例

3.createComponent:ViewContainerRef的方法,内部会调用ComponentFactory实例的create方法创建对应组件,并将组件添加到容器内

export abstract class ComponentFactoryResolver{
  static NULL:ComponentFactoryResolver=new _NullComponentFactoryResolver();
  abstract resolverComponentFactory<T>(component:Type<T>):ComponentFactory<T>
}
@Component({
 selector:'helping',
template:`//创建模板,那模板的内容就不是固定的
   <ng-container #con></ng-container>
   `
})
export class helpingComponent{

  @ViewChild('con',{read:ViewContainerRef})
  conRef:ViewContainerRef; 
 
  constractor(private factoryResolver: ComponentFactoryResolver,
              private helpingService: HelpingService){
     this.helpingService.manualChange.subscribe((comp)=>{
      this.loadComp(comp);
    })

   private loadComp(comp){
     this.conRef.clear();//删除之前的视图
     let factory=this.factoryResolver.resolveComponentFactory(comp);//factory是一个如何创建组件的实例
     this.conRef.createComponent(factory)    }
 }