Create host Views(components) and embedded Views( ng-templates)
1.Angular 2 支持的 View(视图) 类型有哪几种 ?
- Embedded Views – Template 模板元素
- Host Views – Component 组件
1.1 如何创建 Embedded View
import { Component, TemplateRef, ViewChild, ViewContainerRef, AfterViewInit } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<h1>Welcome to Angular World</h1>
<template #tpl>
<span>I am span in template</span>
</template>
`,
})
export class AppComponent {
@ViewChild('tpl')
tplRef: TemplateRef<any>;
@ViewChild('tpl', { read: ViewContainerRef })
tplVcRef: ViewContainerRef;
ngAfterViewInit() {
// console.dir(this.tplVcRef); (1)
this.tplVcRef.createEmbeddedView(this.tplRef);
}
}
1.2 如何创建 Host View
constructor(private injector: Injector,
private r: ComponentFactoryResolver) {
let factory = this.r.resolveComponentFactory(AppComponent);
let componentRef = factory.create(injector);
let view = componentRef.hostView;
}
import {
Component, ViewChild, ViewContainerRef, ComponentFactory,
ComponentRef, ComponentFactoryResolver, OnDestroy
} from '@angular/core';
import { AlertComponent } from './exe-alert.component';
@Component({
selector: 'exe-app',
template: `
<ng-template #alertContainer></ng-template>
<button (click)="createComponent('success')">Create success alert</button>
<button (click)="createComponent('danger')">Create danger alert</button>
`
})
export class AppComponent implements OnDestroy {
componentRef: ComponentRef<AlertComponent>;
@ViewChild("alertContainer", { read: ViewContainerRef }) container: ViewContainerRef;
constructor(private resolver: ComponentFactoryResolver) { }
createComponent(type: string) {
this.container.clear();
const factory: ComponentFactory<AlertComponent> =
this.resolver.resolveComponentFactory(AlertComponent);
this.componentRef = this.container.createComponent(factory);
this.componentRef.instance.type = type;
this.componentRef.instance.output.subscribe((msg: string) => console.log(msg));
}
ngOnDestroy() {
this.componentRef.destroy()
}
}
2.Angular 2 Component 组件中定义的 <template> 模板元素为什么渲染后会被移除 ?
因为 <template> 模板元素,已经被 Angular 2 解析并封装成 TemplateRef 实例,通过 TemplateRef 实例,我们可以方便地创建内嵌视图(Embedded View),我们不需要像开篇中的例子那样,手动操作 <template> 模板元素。
3.ViewRef 与 EmbeddedViewRef 之间有什么关系 ?
ViewRef 用于表示 Angular View(视图),视图是可视化的 UI 界面。EmbeddedViewRef 继承于 ViewRef,用于表示 <template> 模板元素中定义的 UI 元素。
ViewRef
// @angular/core/src/linker/view_ref.d.ts
export declare abstract class ViewRef {
destroyed: boolean;
abstract onDestroy(callback: Function): any;
}
EmbeddedViewRef
// @angular/core/src/linker/view_ref.d.ts
export declare abstract class EmbeddedViewRef<C> extends ViewRef {
context: C;
rootNodes: any[]; // 保存<template>模板中定义的元素
abstract destroy(): void; // 用于销毁视图
}
export declare class ViewContainerRef_ implements ViewContainerRef {
...
length: number; // 返回视图容器中已存在的视图个数
element: ElementRef;
injector: Injector;
parentInjector: Injector;
// 基于TemplateRef创建内嵌视图,并自动添加到视图容器中,可通过index设置
// 视图添加的位置
createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C,
index?: number): EmbeddedViewRef<C>;
// 基 ComponentFactory创建组件视图
createComponent<C>(componentFactory: ComponentFactory<C>,
index?: number, injector?: Injector, projectableNodes?: any[][]): ComponentRef<C>;
insert(viewRef: ViewRef, index?: number): ViewRef;
move(viewRef: ViewRef, currentIndex: number): ViewRef;
indexOf(viewRef: ViewRef): number;
remove(index?: number): void;
detach(index?: number): ViewRef;
clear(): void;
}
Code samples by Maxim Koretskyi:
@Component({
selector: 'sample',
...export class SampleComponent{
constructor(private hostElement: ElementRef) {
//outputs <sample>...</sample>
console.log(this.hostElement.nativeElement.outerHTML);
}
@Component({
selector: 'sample',
template: `
<span #tref>I am span</span>
`
})
export class SampleComponent implements AfterViewInit {
@ViewChild("tref", {read: ElementRef}) tref: ElementRef;
ngAfterViewInit(): void {
// outputs `I am span`
console.log(this.tref.nativeElement.textContent);
}
}
@Component({
selector: 'sample',
template: `
<template #tpl>
<span>I am span in template</span>
</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);
}
}
ngAfterViewInit() {
let view = this.tpl.createEmbeddedView(null);
}
/**In Angular, each component is bound to a particular instance of an injector,
so we’re passing the current injector instance when creating the component.
Also, don’t forget that components that are instantiated dynamically
must be added to EntryComponents of a module or hosting component.
**/
constructor(private injector: Injector,
private r: ComponentFactoryResolver) {
let factory = this.r.resolveComponentFactory(ColorComponent);
let componentRef = factory.create(injector);
let view = componentRef.hostView;
}
@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);
}
}
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
}
@Component({
selector: 'sample',
template: `
<span>I am first span</span>
<ng-container #vc></ng-container>
<span>I am last span</span>
<template #tpl>
<span>I am span in template</span>
</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);
}
}
<sample>
<span>I am first span</span>
<!--template bindings={}-->
<span>I am span in template</span>
<span>I am last span</span>
<!--template bindings={}-->
</sample>
/**
To remove a view from the DOM, we can use detach method.
All other methods are self explanatory and can be used to get a reference to a view by the index,
move the view to another location or remove all views from the container.
**/
class ViewContainerRef {
element: ElementRef
length: number
createComponent(componentFactory...): ComponentRef<C>
createEmbeddedView(templateRef...): EmbeddedViewRef<C>
...
}
/** ngTemplateOutlet and ngComponentOutlet **/
/** While it’s always good to know how the underlying mechanism works,
it’s usually desirable to have some sort of a shortcut.
This shortcut comes in a form of two directives: ngTemplateOutlet and ngComponentOutlet.
**/
@Component({
selector: 'sample',
template: `
<span>I am first span</span>
<ng-container [ngTemplateOutlet]="tpl"></ng-container>
<span>I am last span</span>
<template #tpl>
<span>I am span in template</span>
</template>
`
})
export class SampleComponent {}
<ng-container *ngComponentOutlet="ColorComponent"></ng-container>