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) } }