インフラジスティックス・ジャパン株式会社Blog

インフラジスティックス・ジャパン株式会社のチームメンバーが技術トレンド、製品Tips、サポート情報からライセンス、日々の業務から感じることなど、さまざまなトピックについてお伝えするBlogです。

Angularの変更検知とその仕組み

Angularの最大の強みの1つは、アプリケーションの変更を簡単に検出・更新し、更新された状態を自動的に画面にレンダリングする機能です。 より深くAngularの変更検知を理解するために、どのように変更検知が動作しているか、開発者がどのように利用しコードを実装するかについて、いくつかの例を提供し、この記事で以下のトピックについて見てみたいと思います。

この記事はインフラジスティックス本社により作成された英文記事の翻訳により作成されました。
原文は以下よりご確認いただけます。
www.infragistics.com

Angular の変更検知とは?

Angularの変更検知は、アプリの任意のコンポーネントでデータが変更されたことを検出し、ビューを再レンダリングするメカニズムです。更新された値またはオブジェクトをすぐに更新して、エンドユーザーに表示します。このように、AngularはUIがソフトウェアの内部状態と同期していることを常に監視しています。コンポーネントとビューが常に同期しているのです。

変更はさまざまな場面で発生し、さまざまなイベントから派生します。

  • ネットワークリクエストやコンポーネントイベントから受け取ったデータ
  • マウスクリック、スクロール、マウスオーバー、キーボードナビゲーション
  • AJAXの呼び出し
  • setTimeOut、SetInterval などの JavaScript タイマー関数の使用

Angular の変更検知はどのように動作するのか?

デフォルトでは、Angularは、何かアプリに変化がおこると、すべてのコンポーネント(上から下へ)に対して変更検知を行います 。(前に述べたように、ユーザーイベントか、ネットワークリクエストから受け取ったデータかなにかが発生元だとします。)変更されたデータを検出してDOMを更新するために、フレームワークは各コンポーネントに独自の変更検出器を提供します。変更検出器はテンプレート上のバインディングを読み取り、更新されたデータをビューに反映し、データモデルと DOM の両方が同期していることを確認します。

例えば、コンポーネントのデータバインディングを更新し、データモデルを更新したい場合があります。Angularの変更検知はトリガーされた変更を検出し、変更検知を実行してコンポーネントツリー内のすべてのコンポーネントを上から下へとチェックします。このようにして、対応するモデルが変更されたかどうかを検証します。そして、新しい値があった場合、即座にDOMを更新します。

オンラインフォームに入力する際に、ユーザーが住所変更ボタンをクリックしたとします。このアクションは自動的に変更検出ツリー内のすべてのViewに対して変更検出をトリガーします。Angularの変更検出器は、変更をチェックするすべてのViewを収集し、最初にこの変更を要求したユーザーの住所の値を更新します。

Angular の変更検知の仕組み

開発者として、私たちはAngularアプリが完璧に動作し、高性能で、素晴らしいUXを提供するために様々な要件に適合しなければならないことを知っています。つまり、内部モデルの状態が常にビューと同期している必要があります。そのため、パフォーマンスを最適化したいときはいつでも、データが変更されるたびにDOMを更新できる、Angularの変更検知の方法を採用します。

フレームワークが提供するAngularの変更検知の方法は2つあります。

  • デフォルトの変更検知
  • OnPush型の変更検知

デフォルトの変更検知

Angular の変更検知がデフォルトの設定をされている場合、モデルのプロパティに変更があると、Angular は DOM を更新するためにコンポーネントツリーを横断して変更検出を行います。各コンポーネントはアプリケーションの起動時に作成される変更検出器を持っています。

ChangeDetectorRef クラスは、変更検出ツリーを操作するために使用できるいくつかの組み込みメソッドを提供します。

  • markForCheck() - 変更されたコンポーネントをマークし、更新のために再度チェックすることができます。
  • detach() - 変更検出ツリーからビューを除外します。つまり、ビューが再び接続されるまで、チェックされません。
  • detectChanges() - ビューとその子コンポーネントをチェックします。
  • checkNoChanges() - ビューとその子供をチェックし、何らかの変更が検出された場合、エラーを投げます。
  • reattach() - 新しい変更を検出できるように、切り離されたビューを再アタッチします。

ChangeDetectionStrategy.OnPush の場合

Angular の変更検出器がOnPushに設定されている場合、Angularは新しい参照がコンポーネントに渡された時のみ、変更検出を行います。observableがOnPushに渡された場合、Angular の変更検出器がDOMを更新するためには、手動で呼び出す必要があります。

@Component({
  selector: 'app-card',
  templateUrl: './card.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush,
  styleUrls: ['./card.component.scss'] 
  })
  
  export class CardComponent {}
}

OnPush型変更検知のメリット

親要素が@Input()プロパティとして渡されない値を更新している場合、子要素の不要なチェックが行われないため、コンポーネントの再描画が大幅に速くなります。

Ignite UI for Angular 上で変更検知を使用する方法

Ignite UI for Angularを使って、買い物用のアイテムが入ったリストを表示する簡単な例を作ってみましょう。ショッピングリストコンポーネントと、リストに新しいアイテムを追加するための入力を用意します。

import { Component } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
@Component({
     selector: 'app-root',
     templateUrl: './app.component.html',
     styleUrls: ['./app.component.css']
})

export class AppComponent {
    items = new BehaviorSubject(['Apples', 'Tomatoes', 'Potatoes']);

     constructor() { }

     addNewItem(item) {
            this.items.next([...this.items, item]);
     }   
}

app.component.tsでは、いくつかのデフォルト値を持つBehaviorSubjectと、リストに新しいアイテムを追加するメソッドを宣言しています。

<input #newItem type="text" placeholder="Add new item">
<button (click)="addNewItem(newItem.value)">Add Item</button>
<shopping-items [data]="items"></shopping-items>

続けて、igx-list コンポーネントを使用してアイテムを表示する子コンポーネント shopping-items コンポーネントを作成します。

<h5>Shopping List</h5>
<igx-list class="list" *ngFor="let item of shoppingItems">
  <igx-list-item>
    <span>{{item}}</span>
  </igx-list-item>
</igx-list>
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input } from '@angular/core';
import { Observable } from 'rxjs';
@Component({
  selector: 'shopping-items',
  templateUrl: './child.component.html',
  styleUrls: ['./child.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})

export class ShoppingList {
  @Input() data: Observable<string[]> | undefined;
  shoppingItems: string[] = [];
  constructor(private changeDetector: ChangeDetectorRef) { }

  ngOnInit() {
    if (this.data) {
      this.data.subscribe(item => {
        this.shoppingItems = item;
        this.changeDetector.markForCheck();
      });
    }
  }
}

ここでChangeDetectorRefからmarkForCheck()メソッドを使わないと、Angularは変更検知の実行を拒否します。そのため、手動で行う必要があります。このメソッドで、特定の入力が変異したときに変更検出を実行するようにAngularに伝えます。

まとめ

プロジェクトが大きくなればなるほど、速度低下が発生する可能性があります。Angularの変更検知は、アプリの効率を上げるのに役立つテクニックです。そのため、大規模なプロジェクトでは、ChangeDetectionStrategy.OnPushを使用した方が、パフォーマンスを節約できる可能性があります。

Ignite UI for Angular ad 202211

技術サポート・無料オンライン相談会をご利用ください

インフラジスティックスのUI製品は多くの機能を備えているためドキュメントの情報量も多く、なかなかお探しの情報にたどり着けない場合もあります。そういった際はお気軽に技術サポートや、製品導入支援担当との無料オンライン相談会をご予約いただくことで検証時間を節約可能ですので、ぜひご活用ください。
技術サポートへの問い合わせ方法を確認する
無料オンライン相談会を予約する