AngularのViewChild()の値はビューの初期化まではundefinedである
どうも、最近小規模な失敗談が尽きません…😭 今回はその中から、フレームワーク仕様の理解不足による失敗談を晒していきたいと思います!
ハマったこと
とあるComponentでViewChild()デコレータで取得したプロパティを参照しようとしたところ、undefinedがだった為にその後の処理で例外になった。
今回、以下のソースコードのような内容を書いていました。(実際のコードとは異なります)
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, AfterViewInit {
title = 'sample';
@ViewChild('foo') fooElement;
ngOnInit() {
// コンポーネントの初期化時にthis.fooElementを参照したかった
console.log(this.fooElement.nativeElement.id);
}
}
this.fooElementにはテンプレートで定義したHTMLElementが代入される…はずでした。

しかし、this.fooElement = undefinedだった為、参照エラーに。
ViewChild()とは
テンプレートで定義した要素や子コンポーネントを参照し、プロパティに格納したい時に使うデコレータです。 親コンポーネント側から子コンポーネントのメソッドやプロパティを呼びたい時や、特定要素の値を参照したい時によく使います。
何故undefinedだったのか
ViewChild()で参照しようとしている値が代入されるタイミングは、ビューの初期化が完了した後だった為です。
ビューの初期化が完了した後というのは、ライフサイクルメソッドであるngAfterViewInit()が呼び出されたタイミングということになります。
↓ライフサイクルメソッドについては公式を確認下さい。
https://delete-circleci-and-firebase--angular-ja.netlify.app/guide/lifecycle-hooks
先ほどのソースコードでは、ライフサイクルメソッドngOnInit()の中でViewChild()した値を参照しようとしていました。
しかし、ngOnInit()はngAfterViewInit()より前の段階で呼び出されるメソッド(=ビューの初期化が未完了の状態)である為、
undefinedとして処理が進行してしまっていたわけです。
修正後
import { AfterViewInit, Component, OnInit, ViewChild } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, AfterViewInit {
title = 'sample';
@ViewChild('foo') fooElement;
ngOnInit() {
// ngOnInit()内ではthis.fooElementは参照しない
}
ngAfterViewInit() {
// ビューの初期化が完了してからthis.fooElementを参照する
console.log(this.fooElement.nativeElement.id);
}
}

無事、this.fooElement.nativeElement.idを参照することができました。
ViewChild()は便利なデコレータですが、代入されるタイミングはしっかり意識しながらコーディングが必要です🤤