Nerd For Tech
Published in

Nerd For Tech

[angular] All about the ViewContainerRef

Photo by Erda Estremera on Unsplash

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.

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>
deprecated…

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);

:)

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store