【Angular】@ViewChildデコレーターの使い方をわかりやすく解説!

この記事ではAngularの『@ViewChildデコレーター』について、

  • Angularの「@ViewChildデコレーター」とは
  • Angularの「@ViewChildデコレーター」の使い方
  • Angularの「@ViewChildデコレーター」の注意点
  • Angularの「@ViewChildデコレーター」と「getElementById」の違い

などを分かりやすく説明するように心掛けています。ご参考になれば幸いです。

Angularの「@ViewChildデコレーター」とは

Angularの@ViewChildデコレーターを使用すると、子コンポーネント、DOM要素、またはディレクティブへの参照を取得できます。これにより、子コンポーネントのプロパティやメソッドにアクセスしたり、直接DOM要素を操作したりすることができます。

Angularの「@ViewChildデコレーター」の使い方

Angularの@ViewChildデコレーターの使い方について、以下の内容を順番に説明します。

  • 子コンポーネントへの参照を取得する方法
    • 親コンポーネントが複数の子コンポーネントを含む場合の使い方
  • DOM要素への参照を取得する方法

子コンポーネントへの参照を取得する方法

今回作成した子コンポーネント(child.component.ts)のサンプルコードを以下に示します。

import { Component } from '@angular/core';

@Component({
  selector: 'app-child',
  template: '<p>子コンポーネント</p>',
})
export class ChildComponent {
  public message: string = 'Hello from Child Component!';
}

今回作成した親コンポーネント(parent.component.ts)のサンプルコードを以下に示します。@ViewChildデコレーターを使って、子コンポーネントへの参照をコンポーネント名(今回はChildComponent)で取得しています。

import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ChildComponent } from './child.component';

@Component({
  selector: 'app-parent',
  template: ` <app-child></app-child> `,
})
export class ParentComponent implements AfterViewInit {
  @ViewChild(ChildComponent) childComponent!: ChildComponent;

  ngAfterViewInit() {
    console.log(this.childComponent.message); // 'Hello from Child Component!'
  }
}

上記のサンプルコードでは、@ViewChildデコレーターを使って、ChildComponentへの参照を取得し、ngAfterViewInitライフサイクルフック内でそのmessageプロパティをコンソールに出力しています。

親コンポーネントが複数の子コンポーネントを含む場合の使い方

親コンポーネントが複数の子コンポーネントを含む場合、それぞれの子コンポーネントに異なるテンプレート参照変数を割り当て、それぞれに@ViewChildを使用します。今回作成した子コンポーネント(child.component.ts)のサンプルコードを以下に示します。

import { Component, Input } from '@angular/core';

@Component({
  selector: 'app-child',
  template: '<p>{{message}}</p>',
})
export class ChildComponent {
  @Input() message: string | undefined;
}

今回作成した親コンポーネント(parent.component.ts)のサンプルコードを以下に示します。@ViewChildデコレーターを使って、子コンポーネントへの参照をテンプレート参照変数(今回は、firstChildsecondChild)で取得しています。

import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { ChildComponent } from './child.component';

@Component({
  selector: 'app-parent',
  template: `
    <app-child #firstChild [message]="'Hello from firstChild!'"></app-child>
    <app-child #secondChild [message]="'Hello from secondChild!'"></app-child>
  `,
})
export class ParentComponent implements AfterViewInit {
  @ViewChild('firstChild') firstChild!: ChildComponent;
  @ViewChild('secondChild') secondChild!: ChildComponent;

  ngAfterViewInit() {
    console.log(this.firstChild.message); // 'Hello from firstChild!'
    console.log(this.secondChild.message); // 'Hello from secondChild!'
  }
}

上記のサンプルコードにおいて、親コンポーネント(parent.component.ts)は、2つの子コンポーネントを持ち、それぞれに異なるメッセージを@Inputデコレーターで渡しています。また、テンプレート参照変数(firstChildsecondChild)を使って、それぞれの子コンポーネントへの参照を取得し、ngAfterViewInitライフサイクルフック内でそのmessageプロパティをコンソールに出力しています。

あわせて読みたい

Angularの『@Inputデコレーター』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。

DOM要素への参照を取得する方法

今回作成したコンポーネント(app.component.ts)のサンプルコードを以下に示します。DOM要素を取得するためには、取得したいDOM要素にテンプレート参照変数を割り当て、@ViewChildデコレーターを使用します。

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

@Component({
  selector: 'app-root',
  template: `
    <input #inputElement type="text" />
    <button (click)="logInputValue()">値をログに出力</button>
  `,
})
export class AppComponent {
  @ViewChild('inputElement') inputElement!: ElementRef;

  logInputValue() {
    console.log(this.inputElement.nativeElement.value);
  }
}

上記のサンプルコードでは、@ViewChildデコレーターを使用して、テンプレート参照変数inputElementを持つ<input>要素への参照を取得しています。logInputValueメソッドでは、その値をコンソールに出力します。

Angularの「@ViewChildデコレーター」の注意点

Angularの@ViewChildデコレーターの注意点を以下に示します。

  • ビューの初期化前はundefinedである

上記の注意点についてこれから説明します。

ビューの初期化前はundefinedである

@ViewChildデコレーターで取得した参照は、コンポーネントのビューの初期化前にはundefinedです。従って、ngAfterViewInitライフサイクルフックの中でアクセスする必要があります。

今回作成した子コンポーネント(child.component.ts)のサンプルコードを以下に示します。

import { Component } from '@angular/core';

@Component({
  selector: 'app-child',
  template: '<p>子コンポーネント</p>',
})
export class ChildComponent {
  public message: string = 'Hello from Child Component!';
}

今回作成した親コンポーネント(parent.component.ts)のサンプルコードを以下に示します。

import { Component, ViewChild, OnInit, AfterViewInit } from '@angular/core';
import { ChildComponent } from './child.component';

@Component({
  selector: 'app-parent',
  template: ` <app-child></app-child> `,
})
export class ParentComponent implements OnInit, AfterViewInit {
  @ViewChild(ChildComponent) childComponent!: ChildComponent;

  ngOnInit() {
    console.log(this.childComponent?.message); // undefined
  }
  ngAfterViewInit() {
    console.log(this.childComponent.message); // 'Hello from Child Component!'
  }
}

ngOnInitライフサイクルフック内では、@ViewChildデコレーターで取得した参照はundefinedです。しかし、ngAfterViewInitライフサイクルフック内では、コンポーネントのビューの初期化が完了しているため、参照にアクセスすることができます。

staticオプションの使用

デフォルトでは、@ViewChildはコンポーネントのビューが初期化された後に参照を取得できますが、staticオプションをtrueに設定することで、ビュー初期化前にも参照を取得できるようになります。以下に親コンポーネント(parent.component.ts)のサンプルコードを以下に示します。

import { Component, ViewChild, OnInit, AfterViewInit } from '@angular/core';
import { ChildComponent } from './child.component';

@Component({
  selector: 'app-parent',
  template: ` <app-child></app-child> `,
})
export class ParentComponent implements OnInit, AfterViewInit {
  @ViewChild(ChildComponent, { static: true }) childComponent!: ChildComponent;

  ngOnInit() {
    console.log(this.childComponent?.message); // 'Hello from Child Component!'
  }
  ngAfterViewInit() {
    console.log(this.childComponent.message); // 'Hello from Child Component!'
  }
}

上記のサンプルコードに示すように、@ViewChild{ static: true }オプションを指定することで、ngOnInitライフサイクルフック内でも参照を取得できます。これにより、ビューの初期化前にアクセスできるようになります。

Angularの「@ViewChildデコレーター」と「getElementById」の違い

@ViewChildデコレーターとdocument.getElementByIdの違いについて説明します。どちらもDOM要素への参照を取得するために使用することができますが、主に以下の特徴が異なります。

  • 要素の参照タイミング
    • @ViewChildデコレーターはAngularのライフサイクルに基づいて動作し、コンポーネントのビューが初期化された後に参照を取得します。そのため、AngularのライフサイクルフックであるngAfterViewInitの中で取得した子コンポーネントやDOM要素を使用することができます。
    • document.getElementByIdも同様にngAfterViewInitの中で使用することができますが、document.getElementByIdはネイティブなDOM操作であり、Angularのライフサイクルに依存しないため、DOMが初期化された後に要素を手動で取得する必要があります。
  • 型安全性
    • @ViewChildデコレーターは特定のAngularコンポーネント、ディレクティブ、またはテンプレート参照変数に対して型安全な方法でアクセスできます。
    • document.getElementByIdは、取得する要素の型がHTMLElementElementなどの汎用的な型に限られるため、具体的な要素のプロパティやメソッドにアクセスする際に型安全性が失われることがあります。

以下のコードは、@ViewChildデコレーターを使ってDOM要素を取得し、その値をログに出力する例です。

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

@Component({
  selector: 'app-root',
  template: `
    <input #inputElement type="text" placeholder="Enter text" />
    <button (click)="logValue()">ログを表示</button>
  `,
})
export class AppComponent implements AfterViewInit {
  @ViewChild('inputElement') inputElement!: ElementRef;

  ngAfterViewInit() {
    // `@ViewChild`が`ngAfterViewInit`で利用可能になります
    console.log('ViewChild inputElement:', this.inputElement);
  }

  logValue() {
    console.log('ViewChild input value:', this.inputElement.nativeElement.value);
  }
}

このコードは、@ViewChildデコレーターを使用して、テンプレート参照変数inputElementを持つ<input>要素への参照を取得しています。ngAfterViewInitでその参照をログに出力し、logValueメソッドでその値をコンソールに出力します。

以下のコードは、document.getElementByIdを使ってDOM要素を取得し、その値をログに出力する例です。

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

@Component({
  selector: 'app-root',
  template: `
    <input id="inputElement" type="text" placeholder="Enter text" />
    <button (click)="logValue()">ログを表示</button>
  `,
})
export class AppComponent implements AfterViewInit {
  inputElement!: HTMLInputElement;

  ngAfterViewInit() {
    // DOMが初期化された後に要素を手動で取得
    this.inputElement = document.getElementById('inputElement') as HTMLInputElement;
    console.log('Native DOM inputElement:', this.inputElement);
  }

  logValue() {
    console.log('Native DOM input value:', this.inputElement.value);
  }
}

このコードは、document.getElementByIdを使用して、IDがinputElementである<input>要素への参照を取得しています。ngAfterViewInitでその参照をログに出力し、logValueメソッドでその値をコンソールに出力します。

本記事のまとめ

この記事ではAngularの『@ViewChildデコレーター』について、以下の内容を説明しました。

  • Angularの「@ViewChildデコレーター」とは
  • Angularの「@ViewChildデコレーター」の使い方
  • Angularの「@ViewChildデコレーター」の注意点
  • Angularの「@ViewChildデコレーター」と「getElementById」の違い

お読み頂きありがとうございました。