angular2+创建宿主视图(动态组件)
创建动态组件前先需要了解一个名词ViewRef,ViewRef表示一个angular视图,在angular中视图(View)是应用程序UI的基本构件。angular鼓励开发人员将UI看作是视图的组成,而不是独立的html标记树。
Angular支持两种视图
Embedded Views which are linked to a Template(连接到模板的嵌入式视图)
-----连接到模板的嵌入视图,在组件模板元素中添加模板(DOM元素、DOM元素组)
Host Views which are linked to a Component(连接到组建的宿主视图)
-----连接到组件的嵌入视图,在组件元素中添加别的组件
目前我们只讨论创建宿主视图。
Creating host view(创建宿主视图)
当组件被动态实例化时,会创建宿主视图,使用ComponentFactoryResolver 可以动态的创建一个组件。
在angular中每个组件都要被绑定到一个注入器(inject)的特定实例,因此我们在创建组件时传递当前的注入器实例,而且需要将动态实例化的组件添加到模块或托管组件的EnterComponents中。视图创建完毕后我们可以使用ViewContainer将其插入到DOM中。
ViewContainerRef
表示一个容器,其中可以附加一个或多个视图。
这里要提到的第一件事是,任何DOM元素都可以用作视图容器。有趣的是,Angular 在元素内部没有插入视图,而是在元素绑定到 ViewContainer 之后附加它们。这类似于 router-outlet 插入组件。
通常,一个好的候选对象可以标记一个 ViewContainer 应该被创建的位置,它是 ng-container 元素。它是作为一个注释呈现的,因此它不会向DOM引入冗余的html元素。下面是一个 ViewContainer 的示例:
@Component({ selector: 'sample', template: ` <span>I am first span</span> <ng-container #vc></ng-container> <span>I am last span</span> ` })export class SampleComponent implements AfterViewInit { @ViewChild("vc", {read: ViewContainerRef}) vc: ViewContainerRef; ngAfterViewInit(): void { // outputs `template bindings={}` console.log(this.vc.element.nativeElement.textContent); } }
正如其他DOM抽象一样, ViewContainer 被绑定到通过 element 属性访问的特定DOM元素。在这个例子中,它绑定到 ng-container 元素作为注释,因此输出是 template bindings={} 。
Manipulating views (操作视图)
ViewContainer为操作视图提供了一个方便的api
class ViewContainerRef { ...clear() : void insert(viewRef: ViewRef, index?: number) : ViewRef get(index: number) : ViewRef indexOf(viewRef: ViewRef) : number detach(index?: number) : ViewRef move(viewRef: ViewRef, currentIndex: number) : ViewRef }
我们前面已经看到了如何从模板和组件手动创建两种视图。一旦我们有了视图,我们就可以使用insert方法将它 insert 到DOM中。
ViewContainer还提供了自动创建视图的API
class ViewContainerRef { element: ElementRef length: number createComponent(componentFactory...): ComponentRef<C> createEmbeddedView(templateRef...): EmbeddedViewRef<C> ... }
下面是自动创建视图的例子
import { Component, ViewChild, ComponentFactoryResolver, Input, ComponentRef, OnInit, AfterViewInit } from "@angular/core"; @Component({ selector: 'modal-host', templateUrl: './modal-host.component.html', styleUrls: ['./modal-host.component.scss'] }) export class ModalHostComponent{ isShow: boolean = false; @ViewChild('container') modalContentHost: ViewContainerRef; constructor( private componentFactoryResolver: ComponentFactoryResolver, ) { } private _contentComponent: any; @Input('content-data') contentData: any; @Input('content-component') set contentComponent(value: any) { this._contentComponent = value; if (!!this._contentComponent) { this.loadComponent(); } else { this.clearComponent(); } } get contentComponent() { return this._contentComponent; } private componentRef: ComponentRef<any>; loadComponent() { setTimeout(()=>{ this.isShow = true; },10) let componentFactory = this.componentFactoryResolver.resolveComponentFactory(this._contentComponent); this.modalContentHost.viewContainerRef.clear(); this.componentRef = this.modalContentHost.viewContainerRef.createComponent(componentFactory); if (!!this.contentData) { this.componentRef.instance.data = this.contentData; } } clearComponent() { if (!!this.modalContentHost) { this.modalContentHost.viewContainerRef.clear(); } this.isShow = false; } close() { } stopPropagation($event) { $event.stopPropagation(); } }
ngComponentOutlet
该指令类似于 ngTemplateOutlet,其不同之处在于它创建了一个宿主视图(实例化一个组件),而不是一个嵌入式视图,该指令在angular4+中开始出现。你可以这样使用:
<ng-container *ngComponentOutlet="ColorComponent"></ng-container>