AngularのNgZoneとは?役割と使い方をわかりやすく解説!

Angularを使ってアプリケーションを開発していると、NgZoneというモジュールに出会うことがあります。

この記事ではAngularの『NgZone』について、以下の内容をサンプルコードを用いてわかりやすく解説します。

  • AngularとZone.jsの関係
  • NgZoneとは
  • AngularのZone内で処理を実行する方法

AngularとZone.jsの関係

Angularでは、非同期処理(例えばsetTimeoutPromise)が完了すると、自動的に変更検知(Change Detection)が行われ、画面が最新の状態に更新されます。この仕組みの背後にあるのが、Angularが内部で利用しているZone.jsというライブラリです。

Zone.jsはブラウザの非同期処理をフックして、非同期処理の完了を追跡しています。これにより、Angularは非同期処理が終わったことを知り、変更検知をトリガーして画面を更新することができます。

NgZoneとは

NgZoneは、AngularがZone.jsをより簡単に扱えるようにするためのモジュールです。NgZoneを使うことで、以下のような操作が可能になります。

  • AngularのZone内で処理を実行する
    • 変更検知を確実にトリガーするコードを実行します。
    • AngularのZone内で処理を実行させるためには、run()メソッドを使用します。
    • たとえば、外部ライブラリ(例:Google Maps API)が非同期操作を行い、その結果をAngularが認識できない場合、run()メソッドを使用して手動で変更検知をトリガーします。
  • AngularのZone外で処理を実行する
    • パフォーマンス向上のため、変更検知をトリガーしないコードを実行します。
    • AngularのZone外で処理を実行するためには、runOutsideAngular()メソッドを使用します。
    • たとえば、頻繁に発生する処理で変更検知を避けたい場合、runOutsideAngular()メソッドを使用します。

AngularのZone内で処理を実行する方法

以下にNgZoneを使ったサンプルコードを示します。ボタンをクリックするたびにカウントアップする処理を記載していますが、run()メソッドを使う場合と使わない場合の動作が異なります。また、runOutsideAngular()メソッドを使用してAngularのZone外で非同期処理を実行するようにしています。

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

@Component({
  selector: 'app-root',
  template: `
    <h1>{{ message }}</h1>
    <button (click)="incrementUseRun()">
      カウントアップ (runメソッドを使用)
    </button>
    <button (click)="increment()">
      カウントアップ (runメソッドを未使用)
    </button>
  `,
})
export class AppComponent {
  counter: number = 0; // カウントを管理する変数
  message: string = '現在のカウント: 0'; // 表示用のメッセージ

  constructor(private ngZone: NgZone) {}

  /**
   * runメソッドを使用してカウントを更新する
   * Angularの変更検知が確実にトリガーされるため、画面が更新される
   */
  incrementUseRun() {
    // runOutsideAngularメソッドを使用してAngularのZone外で非同期処理を実行する
    this.ngZone.runOutsideAngular(() => {
      setTimeout(() => {
        this.counter++; // カウントをインクリメント

        // runメソッドを使用して、AngularのZone内に戻り、変更検知をトリガーする
        // 画面の更新が行われる
        this.ngZone.run(() => {
          this.message = `現在のカウント: ${this.counter}`;
        });
      }, 100); // 0.1秒後に処理を実行
    });
  }

  /**
   * runメソッドを使用せずにカウントを更新する
   * Angularの変更検知がトリガーされないため、画面は更新されない
   */
  increment() {
    // runOutsideAngularメソッドを使用してAngularのZone外で非同期処理を実行する
    this.ngZone.runOutsideAngular(() => {
      setTimeout(() => {
        this.counter++; // カウントをインクリメント

        // runメソッドを使用しないため、変更検知がトリガーされない
        // 画面の更新が行われない
        this.message = `現在のカウント: ${this.counter}`;
        console.log(`Zone外で更新: ${this.message}`);
      }, 100); // 0.1秒後に処理を実行
    });
  }
}

コードの解説

  • incrementUseRun()メソッド
    • runOutsideAngular()メソッドで非同期処理をAngularのZone外で実行しています。
    • run()メソッドを呼び出すことで、AngularのZone内に戻っているため、変更検知をトリガーでき、結果として画面が更新されます。
  • increment()メソッド
    • 同じく非同期処理をZone外で実行しています。
    • run()メソッドを呼び出していないため、変更検知がトリガーされず、画面は更新されません。

また、以下にGoogle Maps APIを使った地図アプリで、マーカーをクリックしたときに変更検知を有効にするサンプルコードを示しています。

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

@Component({
  selector: 'app-map',
  template: `
    <h1>{{ message }}</h1>
    <div id="map" style="width: 100%; height: 400px;"></div>
  `,
})
export class MapComponent {
  message: string = 'マーカーをクリックしてください';

  constructor(private ngZone: NgZone) {}

  ngOnInit() {
    const map = new google.maps.Map(document.getElementById('map')!, {
      center: { lat: 35.6895, lng: 139.6917 },
      zoom: 10,
    });

    const marker = new google.maps.Marker({
      position: { lat: 35.6895, lng: 139.6917 },
      map,
      title: '東京タワー',
    });

    marker.addListener('click', () => {
      this.ngZone.run(() => {
        this.message = 'マーカーがクリックされました!';
      });
    });
  }
}

marker.addListener('click', callback)の中では、Google MapsのAPIが非同期的に実行されるため、AngularのZone外で動作します。ngZone.run()の中でmarker.addListener('click', callback)を呼び出すことで、マーカーがクリックされた際の処理をAngularのZone内に戻し、変更検知をトリガーしています。

本記事のまとめ

この記事ではAngularの『NgZone』について、以下の内容を説明しました。

  • Zone.jsとAngularの関係
    • AngularはZone.jsを利用して非同期処理をフックし、変更検知を自動でトリガーする。
    • 非同期処理(例: setTimeoutPromise)が完了すると、画面が自動更新される。
  • NgZoneとは
    • Zone.jsを簡単に扱えるようにするAngularのモジュール。
    • Angularアプリでの非同期処理の制御や、変更検知のトリガーをサポートする。
  • NgZoneの主なメソッド
    • run()
      • AngularのZone内で処理を実行。
      • 確実に変更検知をトリガーし、画面を更新。
      • 外部ライブラリの非同期処理結果をAngularが認識する場合に使用。
    • runOutsideAngular()
      • AngularのZone外で処理を実行。
      • 変更検知を抑制し、パフォーマンスを向上。
      • 頻繁に発生するイベント(例: スクロール、アニメーション)で変更検知を抑えたい場合に使用。

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