Angular ViewContainerRef TemplateRef ElementRef
Angular: ViewContainerRef, ElementRef, TemplateRef, ComponentRef
Basic introduction to these four concepts
ViewContainerRef
definition from angular Doc: Represents a container where one or more views can be attached to a component, But, this sentense sounds a little strange or meaningless.
keep these points in mind
- any Dom element can be used as a view Container.
- a container is the place where one or more views can be attached to.
- In angular, every time the view is append as the last in the view container.
In conclusion, viewContainer is used to host componentView or embeddedView.
How to get access to ViewContainerRef?
- Through ViewChild, we can get access to ng-container or template variables in html template. we can also get injected from constructor. Let's see the example bellow. remember to add {read: ViewContainerRef} in viewChild
@Component({
selector: 'sample',
template: `
<!-- This is component level viewContainerRef, just for easy to understand -->
<!-- <ng-container> assume -->
<span>I am first span</span>
========================example1=======================
<ng-container #vc>
<span>I am the original Content</span>
<!-- the line bellow will be inserted after running -->
<!-- <div>I am inserted template</div> -->
</ng-container>
=======================================================
====================example2======================
<!-- A Virtual ViewContainerRef will be wrapped around the span -->
<!-- it will equal to -->
<!-- <ng-container> vc1 will refer to this node-->
<span #vc1>I am another view Container</span>
<!-- After the code running the next line will be inserted -->
<!-- <div>I am inserted template</div> -->
<!-- </ng-container> this is not exist, just from imaging -->
==================================================
<ng-template #tmp1><div>I am inserted template</div></ng-template>
<span>I am last span</span>
====================example3====================
<!-- After the code running the next line will be inserted -->
<!-- <div>I am inserted template</div> -->
=================================================
<!-- </ng-container> -->
`
})
export class SampleComponent implements AfterViewInit {
@ViewChild("vc", {read: ViewContainerRef}) example1: ViewContainerRef;
@ViewChild("vc1, {read: ViewContainerRef}) example2: ViewContainerRef;
@ViewChild('tmp1') tmp1:TemplateRef<any>;
constructor(private componentLevelViewRef:ViewContainerRef){}
ngAfterViewInit(): void {
this.example1.createEmbeddedView(this.tmp1);
this.example2.createEmbeddedView(this.tmp1);
this.componentLevelViewRef.createEmbeddedView(this.tmp1);
}
}
- Injected in constructor of Component and Directive
- In Directive, see the code bellow. What does the viewContainer bound to? the div.viewContainer is the viewContainer.
//ts @Directive({ selector:'[direct]', }) export class Directive{ constructor( private viewContainer:ViewContainerRef, ){} ngAfterViewInit(): void { console.log(this.viewContainer.element.nativeElement); } } ============================================ // html <!-- <ng-container> viewContener will refer to this --> <div [direct]> elements is attached by the directiive </div> <!-- embeded template will be appended here one by one --> <!-- </ng-container> -->
What can viewContainerRef do?
here we give a quick property list and method.
- element: ElementRef; it refers to the contents under ng-container, we can access dom by calling viewContainerRef.element.nativeElement.
- injector: Injector; get the injector of current viewContainerRef.
- length: get the length of the views attached under this viewContainerRef. we can assume that viewContainerRef has this data [viewRef,viewRef,viewRef,...]
- createEmbeddedView
(templateRef:TemplateRef ,context?:C,index?:number):EmbeddedViewRef This is an important function. see code bellow.
this.example1.createEmbeddedView(this.tmp1);
this.example2.createEmbeddedView(this.tmp1);
this.componentLevelViewRef.createEmbeddedView(this.tmp1);
- createComponent(componentFactory:ComponentFactory
,index?:number,injector?:Injector,projectableNodes?:any[][],ngModule?:NgModuleRef ):ComponentRef
ElementRef
A wrapper around a native element. it is simple and straightforward.
class ElementRef<T = any> {
constructor(nativeElement: T)
nativeElement: T
}
we often use this in constructor by injecting. see code bellow, for both component and directive are the same behavior.
// typescript
@Directive({
selector:'[my-select]',
})
export class MyDirective{
constructor(public el:ElementRef){}
ngAfterViewInit(): void {
// <div>Hello I am body</div>
console.log(this.el.nativeElement);
}
}
// html
<div my-select>Hello I am body</div>
TemplateRef
it represents an embedded template which can be used to instantiate embedded view. to be honest it is <ng-template>xxx</ng-template>
in html.
how can we get refer to the TemplateRef
We ONLY have one way, through ViewChild, contentChild or injector, we can't new a templateRef instance in the fly.
let's see these examples
// html
<ng-template #sample1>I am Sample1</ng-template>
<ng-template pTemplate>I am Sample2</ng-template>
@Directive({
selector: '[pTemplate]',
host: {
}
})
export class PrimeTemplate {
@Input() type: string;
@Input('pTemplate') name: string;
constructor(public template: TemplateRef<any>) {}
getType(): string {
return this.name;
}
}
// TS
@ViewChild('sample1',{read:TemplateRef}) sample1:TemplateRef<any>;
@ViewChild(PrimeTemplate) sample2:PrimeTemplate;
sample2.template ------------it is the TemplateRef,
@ContentChild('sample3',{read:TemplateRef}) sample3:TemplateRef<any>,
What can TemplateRef do?
-
elementRef:ElementRef, it refer to parent view of this template.
see the picture here
-
createEmbeddedView(context:C) create an embedded view, difference with ViewContainerRef.createEmbeddedView()
// in templateRef
const viewRef = this.templateRef.createEmbeddedView({$implicit:'implicit param'});
this.viewContainerRef.insert(viewRef,this.this.viewContainerRef.length);
// in ViewContainerRef
this.viewContainerRef.createEmbeddedView(this.templateRef,{$implicit:'implicit param'});