[angular] All about the ViewContainerRef
ViewContainerRef(VCR) represents a container where one or more view can be attached.
It is basically a place where all template grammars do a magic.
Most time we write template with mark-ups to make a new view, also we can create view with this VCR on demand.
| How to Access to the ViewContainerRef.
ViewChild can access to a VCR of element on the template by adding token option {read: ViewContainerRef} or to component host, inject dependency on instantiation.
Note, that ng-template also can provide viewContainerRef and that’s how we can create structural directive.
@Component({
selector: 'my-app',
template: `
<h1>See console to See viewContainer</h1>
<ul>
<li><span #span>this is html element</span></li>
<li><custom-comp #custom></custom-comp></li>
<li><ng-template #template [ngIf]="true">this is template</ng-template></li>
</ul>`,})
export class AppComponent implements AfterViewInit {
@ViewChild('span', { read: ViewContainerRef }) span;
@ViewChild('custom', { read: ViewContainerRef }) custom;
@ViewChild('template', { read: ViewContainerRef }) template; constructor(private viewContainerRef: ViewContainerRef) {} ngAfterViewInit() {
console.log('>>>hostElement', this.viewContainerRef);
console.log('>>>htmlElement', this.span);
console.log('>>>customElement', this.custom);
console.log('>>>ngTemplate', this.template);
}}
| Example of creating view by *ngIf
We know ngIf is structural directive. Remove sugar-coated part, they are the same ones.
<div *ngIf="true">hey, show me!!!</div>// is same as the later.<ng-template [ngIf]="true"><div>hey, show me!!!</div></ng-template>
How ngIf changes the template on demand?
private _updateView() {
if (this._context.$implicit) {
// if ngIf is true if (!this._thenViewRef) { // if no thenView created..
this._viewContainer.clear();
this._elseViewRef = null;
if (this._thenTemplateRef) {
this._thenViewRef =
this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
}
}
} else {
// if ngIf is false
if (!this._elseViewRef) { // if no elseView created..
this._viewContainer.clear();
this._thenViewRef = null;
if (this._elseTemplateRef) {
this._elseViewRef = this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context);
}
}
} }
There are two main methods here, and we will look them closer.
- _viewContainer(VCR).createEmbeddedView(…)
- _viewContainer(VCR).clear()
| VCR.createEmbeddedView & VCR.clear
createEmbeddedView creates typeof ViewRef(EmbeddedViewRef extends ViewRef) from TemplateRef.
Get kelly woo’s stories in your inbox
Join Medium for free to get updates from this writer.
ViewRef is in charge of rendering the view, it extends ChangeDetectorRef.
VCR accommodates more than 1 ViewRef, calling createEmbeddedView leads to appending new view to the container instead of replacing.
To remove all the embedded views, call VCR.clear(), existing views will be detached and destroyed.
If you don’t want to destroy view and just remove from the container, VCR.detach is what you are looking for.
| VCR.detach() & VRC.insert()
detach does not destroy view and keep it the element as it is, when you insert it back, any status that angular does not concern can be preserved.
That’s why, in example, yellow background color still remains after reattaching.
| ViewContainerRef’s other methods
From the name of viewContainer, you might think that viewRefs locate inside VCR.element, but they actually come as sibling elements.
ViewContainerRef.element references ElementRef of the view.
It can be custom element, element node, even comment node. That’s why all view locates next to the VCR.element.nativeElement.
Now we know how to create template view, can we create component as well?
Yes, VCR also provide the method of createComponent
Before it required componentFactory but now it is deprecated and you can pass Component itself.
abstract createComponent<C>(componentType: Type<C>, options?: { index?: number; injector?: Injector; ngModuleRef?: NgModuleRef<unknown>; projectableNodes?: Node[][]; }): ComponentRef<C>Now component can be also preserved like template.
// componentRef is returned by createComponent method.this.componentRef = this.viewContainerRef.createComponent(...);// you can detach without destroying
this.viewContainerRef.detach(0);// also reattach it with preserved elements.
this.viewContainerRef.insert(this.componentRef.hostView, 0);
:)







