どうも、最近小規模な失敗談が尽きません…😭 今回はその中から、フレームワーク仕様の理解不足による失敗談を晒していきたいと思います!
ハマったこと
とある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()
が呼び出されたタイミングということになります。
↓ライフサイクルメソッドについては公式を確認下さい。
delete-circleci-and-firebase--angular-ja.netlify.app
先ほどのソースコードでは、ライフサイクルメソッド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()
は便利なデコレータですが、代入されるタイミングはしっかり意識しながらコーディングが必要です🤤