Here is how to get ViewContainerRef before @ViewChild query is evaluated
ViewContainerRef can usually only be used after ngAfterViewInit hook. This article shows how to get access to ViewContainerRef earlier using a directive instead of template reference and ViewChild query.

Here is how to get ViewContainerRef before @ViewChild query is evaluated
ViewContainerRef can usually only be used after ngAfterViewInit hook. This article shows how to get access to ViewContainerRef earlier using a directive instead of template reference and ViewChild query.


In one of my recent article on dynamic component instantiation Here is what you need to know about dynamic components in Angular I’ve shown the way how to add a child component to the parent component view dynamically. All dynamic components are inserted into a specific place in the template using ViewContainerRef
reference. This reference is usually obtained by specifying some template reference variable in the parent component template and then using queries like ViewChild
inside the component to get it.
Here is the quick refresher. Suppose we have our parent App
component and we want to add a child A
component into the specific place in the template. Here is how we do that.
A componentLink to this section
We’re creating A
component:
<>Copy@Component({ selector: 'a-comp', template: ` <span>I am A component</span> `, }) export class AComponent {}
App root moduleLink to this section
And then register it with in the declarations
and entryComponents
:
<>Copy@NgModule({ imports: [BrowserModule], declarations: [AppComponent, AComponent], entryComponents: [AComponent], bootstrap: [AppComponent] }) export class AppModule {}
App componentLink to this section
And then in the parent App
component we put the code that creates A
component instance and inserts it:
<>Copy@Component({ moduleId: module.id, selector: 'my-app', template: ` <h1>I am parent App component</h1> <div class="insert-a-component-inside"> <ng-container #vc></ng-container> </div> `, }) export class AppComponent { @ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef; constructor(private r: ComponentFactoryResolver) {} ngAfterViewInit() { const factory = this.r.resolveComponentFactory(AComponent); this.vc.createComponent(factory); } }
Here is the working plunker. If that’s something you don’t understand, I suggest you read the article I mentioned in the beginning.
This is all well and good but there’s one limitation with that approach. We have to wait until the ViewChild
query is evaluated and that happens during change detection. We can access the reference only after ngAfterViewInit
lifecycle hook. But what if we don’t want to wait until Angular runs change detection and want to have a complete component view before change detection? As it turns out we can do that using a directive instead of template reference and ViewChild
query.
Using directive instead of ViewChild queryLink to this section
Every directive can inject a reference to the ViewContainerRef
into the constructor. This will be a reference to a view container associated with and anchored to the directive host element. So let’s implement such a directive:
<>Copyimport { Directive, Inject, ViewContainerRef } from '@angular/core'; @Directive({ selector: '[app-component-container]', }) export class AppComponentContainer { constructor(vc: ViewContainerRef) { vc.constructor.name === "ViewContainerRef_"; // true } }
I’ve added the check in the constructor to ensure that view container is available when the directive is instantiated. Now we need to use it in the App
component template instead #vc
template reference:
<>Copy<div class="insert-a-component-inside"> <ng-container app-component-container></ng-container> </div>
If you run it you will see that it works fine. Great, we now know how a directive can access the view container before change detection. Now it somehow needs to pass it to the component. How can we do that? Well, a directive can inject a parent component and call a method on the component directly. However, there’s a limitation in that the directive has to know the name of the parent component or use the approach described here.
A better alternative is to use a service shared between a component and its child directives and communicate through it! We can implement that service on the component directly to make it local. I’ll also use a custom string token for simplicity:
<>Copyconst AppComponentService= { createListeners: [], destroyListeners: [], onContainerCreated(fn) { this.createListeners.push(fn); }, onContainerDestroyed(fn) { this.destroyListeners.push(fn); }, registerContainer(container) { this.createListeners.forEach((fn) => { fn(container); }) }, destroyContainer(container) { this.destroyListeners.forEach((fn) => { fn(container); }) } }; @Component({ providers: [ { provide: 'app-component-service', useValue: AppComponentService } ], ... }) export class AppComponent {}
This service simply implements primitive pub/subscribe pattern and notifies subscribes when the container is registered.
Now we can inject that service into the AppComponentContainer
directive and register the view container:
<>Copyexport class AppComponentContainer { constructor(vc: ViewContainerRef, @Inject('app-component-service') shared) { shared.registerContainer(vc); } }
And the only thing that is left is to listen in the App
component when the container is registered and use it to create a component dynamically:
<>Copyexport class AppComponent { vc: ViewContainerRef; constructor(private r: ComponentFactoryResolver, @Inject('app-component-service') shared) { shared.onContainerCreated((container) => { this.vc = container; const factory = this.r.resolveComponentFactory(AComponent); this.vc.createComponent(factory); }); shared.onContainerDestroyed(() => { this.vc = undefined; }) } }
Here is the plunker. And that’s it. You can see that we no longer need a ViewChild
query. And if you add a ngOnInit
lifecycle hook you will see that the A
component is rendered before it’s triggered.
RouterOutletLink to this section
If this approach seems hackish to you it is not. We need to look no further than the sources of Angular router-outlet
directive. This directive injects viewContainerRef
in the constructor and uses the shared service parentContexts
to register itself and a view container within router configuration:
<>Copyexport class RouterOutlet implements OnDestroy, OnInit { ... private name: string; constructor(parentContexts, private location: ViewContainerRef) { this.name = name || PRIMARY_OUTLET; parentContexts.onChildOutletCreated(this.name, this); ... }
Comments (0)
Be the first to leave a comment
About the author

Principal Engineer at kawa.ai.. Founder indepth.dev. Big fan of software engineering, Web Platform & JavaScript. Man of Science & Philosophy.

About the author
Max Koretskyi
Principal Engineer at kawa.ai.. Founder indepth.dev. Big fan of software engineering, Web Platform & JavaScript. Man of Science & Philosophy.
About the author

Principal Engineer at kawa.ai.. Founder indepth.dev. Big fan of software engineering, Web Platform & JavaScript. Man of Science & Philosophy.