Angular @ViewChild and ngIf

The Angular @ViewChild decorator can be tricky to handle if the element or component it references also has an ngIf directive. Take the example below.

Currently, clicking the button will set the display flag is set to true and the inner text of the div is assigned a value.

import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
   <button (click)="onButtonClick()">Display</button>

   <div #contentPlaceholder *ngIf="display" class="content"></div>

   <div class="error" *ngIf="!ref">Reference is undefined</div>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  display = false;
  dynamicContent = '';

  @ViewChild('contentPlaceholder', { static: false })
  ref: ElementRef;

  onButtonClick() {   
    this.display = true;
    this.ref.nativeElement.textContent = 'This is the dynamic content'; 
  }
}

The result of clicking the button is shown below. The reference to @ViewChild is undefined. The reason for the undefined reference is due to the fact view queries are set before the AfterViewInit Angular lifecycle method is called and since the ngIf is intially set to false during initial component property binding, the view query was not executed by Angular change detection.

Getting around this issue is simply to kick off the Angular change detection manually. Inject the chnage detection service into the component and on button click, execute change detection.

import { Component, ViewChild, ElementRef, ChangeDetectorRef }  from '@angular/core';

@Component({
  selector: 'my-app',
  template: `
   <button (click)="onButtonClick()">Display</button>

   <div #contentPlaceholder *ngIf="display" class="content"></div>

    <div class="error" *ngIf="!ref">Reference is undefined</div>
  `,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  display = false;
  dynamicContent = '';

  @ViewChild('contentPlaceholder', { static: false })
  ref: ElementRef;

  constructor(private changeDetector: ChangeDetectorRef) {}

  onButtonClick() {   
    this.display = true;
    this.changeDetector.detectChanges();
    this.ref.nativeElement.textContent = 'This is the dynamic content'; 
  }
}

For source code visit stackblitz.

Dan