The A component

We begin by defining the A component:

@Component({
  selector: 'a-comp',
  template: `
      <span>I am A component</span>
  `,
})
export class AComponent {}

App root module

Next, we declare it in the module's declarations and entryComponents:

@NgModule({
  imports: [BrowserModule],
  declarations: [AppComponent, AComponent],
  entryComponents: [AComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}

App component

Finally, inside the parent App component, we write the logic to instantiate A and insert it into the view:

@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 any part of this feels unclear, I recommend revisiting the article linked at the start.

This method works perfectly well, but it has a constraint: we must defer the insertion until Angular evaluates the ViewChild query, which happens during change detection. The reference becomes accessible only after the ngAfterViewInit lifecycle hook fires. But what if we need the component view fully constructed before change detection ever runs? It turns out a directive can handle this without relying on a template reference variable or a ViewChild query.

Leveraging a directive in place of ViewChild

Any directive is able to inject a ViewContainerRef directly into its constructor. This injected reference points to the view container that is attached to the directive's host element. Let’s build such a directive:

import { Directive, Inject, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[app-component-container]',
})

export class AppComponentContainer {
  constructor(vc: ViewContainerRef) {
    vc.constructor.name === "ViewContainerRef_"; // true
  }
}

I added a check inside the constructor to confirm that the view container exists at the moment the directive is created. Now we update the App component template to use this directive rather than the #vc template reference:

<div class="insert-a-component-inside">
    <ng-container app-component-container></ng-container>
</div>

When you run this, you will see that it behaves as expected. So now we have a way for a directive to obtain the view container prior to any change detection cycle. The next step is getting that container into the hands of the component. How do we achieve that? One option is for the directive to inject the parent component and call one of its methods. The downside is that the directive must then know the exact type of the parent component, or employ the workaround described here.

A more elegant solution is to use a shared service that both the component and its child directives can access. This service can be defined right on the component to keep it scoped locally. For simplicity, I will also use a custom string token:

const 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 implements a basic publish/subscribe pattern, notifying any subscribers as soon as a container is registered.

Now we inject this service into the AppComponentContainer directive and use it to register the view container:

export class AppComponentContainer {
  constructor(vc: ViewContainerRef, @Inject('app-component-service') shared) {
    shared.registerContainer(vc);
  }
}

All that remains is to subscribe to this notification inside the App component and dynamically create the child component once the container is available:

export 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 all there is to it. Notice that we have eliminated the need for a ViewChild query entirely. If you add an ngOnInit lifecycle hook, you will observe that the A component is rendered before that hook is ever called.

RouterOutlet

If this approach feels unconventional, rest assured it is not. Look no further than the source code of Angular's own router-outlet directive. This directive injects viewContainerRef in its constructor and relies on a shared service called parentContexts to register both itself and its view container within the router's internal configuration:

export class RouterOutlet implements OnDestroy, OnInit {
  ...
  private name: string;
  constructor(parentContexts, private location: ViewContainerRef) {
    this.name = name || PRIMARY_OUTLET;
    parentContexts.onChildOutletCreated(this.name, this);
    ...
  }