angular

Dynamic Create Angular Views

2018-09-20  本文已影响1人  forks1990

There are two kinds of view in angular: embedded view and host view.

Embedded View

Embedded view create from a template, can not directly operate on template, but template ref instead.

First define template using ng-template tag embedded in component template string or file:

@Component({
    selector: 'sample',
    template: `
        <ng-template #tpl>
            <span>I am span in template</span>
        </ng-template>
    `
})

Then get template ref:

export class SampleComponent implements AfterViewInit {
    @ViewChild("tpl") tpl: TemplateRef<any>;

Create:


    ngAfterViewInit() {
        let elementRef = this.tpl.elementRef;
        // outputs `template bindings={}`
        console.log(elementRef.nativeElement.textContent);
    }
}

Put them together:

@Component({
    selector: 'sample',
    template: `
        <ng-template #tpl>
            <span>I am span in template</span>
        </ng-template>
    `
})
export class SampleComponent implements AfterViewInit {
    @ViewChild("tpl") tpl: TemplateRef<any>;

    ngAfterViewInit() {
        let elementRef = this.tpl.elementRef;
        // outputs `template bindings={}`
        console.log(elementRef.nativeElement.textContent);
    }
}

Host view

Only entry component can dynamically created, because component may use DI, an instance of Injector needed to do the DI job. Fortunately each component instance bound with an injector, we can get it from ID system.

Here is the sample code:

constructor(private injector: Injector,
            private r: ComponentFactoryResolver) {
    // resolves component factory, btw factory is generated by angular
    // compiler for each our component.
    let factory = this.r.resolveComponentFactory(ColorComponent);
    // create component view, and returns newly created ref of the 
    // component.
    let componentRef = factory.create(injector);
    // get the hosted view.
    let view = componentRef.hostView;
}

Add view to UI

A view itself is useless if not add to component tree, we need view container.

View container is defined using a special tag ng-container like:

        <span>I am first span</span>
        <ng-container #vc></ng-container>
        <span>I am last span</span>

At runtime, <ng-container> rendered as html comment, it is nothing more than a placeholder.

Again, we can not get ViewContainer but ViewContainerRef:

export class SampleComponent implements AfterViewInit {
    @ViewChild("vc", {read: ViewContainerRef}) vc: ViewContainerRef;

Using ViewContainer operate on views:

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
}

As you can see, not just insert views at the placeholder, but do almost anything. Now back to business, insert our dynamic created views:

@Component({
    selector: 'sample',
    template: `
        <span>I am first span</span>
        <ng-container #vc></ng-container>
        <span>I am last span</span>
        <ng-template #tpl>
            <span>I am span in template</span>
        </ng-template>
    `
})
export class SampleComponent implements AfterViewInit {
    @ViewChild("vc", {read: ViewContainerRef}) vc: ViewContainerRef;
    @ViewChild("tpl") tpl: TemplateRef<any>;

    ngAfterViewInit() {
        let view = this.tpl.createEmbeddedView(null);
        this.vc.insert(view);
    }
}

Note: to get ViewContaierRef through @ViewChild, must provide {read: ViewContainerRef} argument, because <ng-container> element itself is not ViewContainer.

To remove view from ViewContainer, call .detach() method.

ViewContainer defines convenient methods to create views:

class ViewContainerRef {
    createComponent(componentFactory...): ComponentRef<C>;
    createEmbeddedView(templateRef...): EmbeddedViewRef<C>;
}
上一篇下一篇

猜你喜欢

热点阅读