概要 • Angular 日本語版
アプリケーションは、ユーザーのログインやプロフィールの更新、機密情報を入力したりその他多くのデータ入力タスクを実行できるようにするために、フォームを使用します。
Angularは、フォームを通じてユーザー入力を処理するために、リアクティブとテンプレート駆動の2つの異なるアプローチを提供します。
どちらもビューからのユーザー入力イベントをキャプチャし、ユーザー入力を検証してフォームモデルとデータモデルを作成して更新し、変更を追跡する方法を提供します。
このガイドは、状況に合わせてどちらのタイプのフォームが最適か判断するのに役立つ情報を提供します。 両方のアプローチで使用される一般的なビルディングブロックを紹介します。 また、2つのアプローチの主な違いをまとめ、セットアップ、データフロー、テストというコンテキストにおけるそれらの違いを示します。
アプローチの選択
リアクティブフォームとテンプレート駆動フォームは、フォームデータを処理および管理する方法が異なります。 それぞれのアプローチは、異なる利点を提供します。
主要な違い
次の表は、リアクティブフォームとテンプレート駆動フォームの主な違いをまとめたものです。
スケーラビリティ
フォームがアプリケーションの中心的な部分である場合は、スケーラビリティが非常に重要です。 フォームモデルをコンポーネント間で再利用できることは不可欠です。
リアクティブフォームは、テンプレート駆動フォームよりもスケーラブルです。 フォームの基礎となるAPIに直接アクセスでき、同期データフロー をビューとデータモデルの間で使用するため、大規模なフォームの作成が容易になります。 リアクティブフォームは、テストのためのセットアップが少なく、テストにはフォームの更新とバリデーションを適切にテストするために変更検知の深い理解は必要ありません。
テンプレート駆動フォームは、単純なシナリオに焦点を当て、再利用性がそれほど高くありません。 フォームの基礎となるAPIを抽象化し、非同期データフロー をビューとデータモデルの間で使用します。 テンプレート駆動フォームの抽象化は、テストにも影響を与えます。 テストは、適切に実行するために手動で変更検知を実行することに大きく依存し、より多くのセットアップが必要です。
フォームモデルのセットアップ
リアクティブフォームとテンプレート駆動フォームの両方とも、ユーザーが対話するフォーム入力要素と、コンポーネントモデル内のフォームデータの間の値の変更を追跡します。 2つのアプローチは、基礎となるビルディングブロックを共有しますが、共通のフォームコントロールインスタンスの作成と管理方法が異なります。
共通のフォーム基盤クラス
リアクティブフォームとテンプレート駆動フォームの両方は、次のベースクラスに基づいて構築されています。
リアクティブフォームでのセットアップ
リアクティブフォームでは、フォームモデルをコンポーネントクラスに直接定義します。
[formControl] ディレクティブは、明示的に作成された FormControl インスタンスを、内部の値アクセサーを使用して、ビュー内の特定のフォーム要素にリンクします。
次のコンポーネントは、リアクティブフォームを使用して、単一コントロールの入力フィールドを実装しています。
この例では、フォームモデルは FormControl インスタンスです。
IMPORTANT: リアクティブフォームでは、フォームモデルが真実の源です。<input> 要素の [formControl] ディレクティブを通じて、特定の時点におけるフォーム要素の値とステータスを提供します。
テンプレート駆動フォームでのセットアップ
テンプレート駆動フォームにおいてフォームモデルは暗黙的であり、明示的ではありません。
NgModel ディレクティブは、特定のフォーム要素の FormControl インスタンスを作成および管理します。
次のコンポーネントは、テンプレート駆動フォームを使用して、単一コントロールの同じ入力フィールドを実装しています。
import {Component, signal} from '@angular/core';import {FormsModule} from '@angular/forms';@Component({ selector: 'app-template-favorite-color', template: ` Favorite Color: <input type="text" [(ngModel)]="favoriteColor" /> `, imports: [FormsModule],})export class FavoriteColorTemplate { favoriteColor = signal('');}
IMPORTANT: テンプレート駆動フォームでは、真実の源はテンプレートです。NgModel ディレクティブは、FormControl インスタンスを自動的に管理します。
フォーム内のデータフロー
アプリケーションにフォームが含まれている場合、Angularはビューをコンポーネントモデルと同期させ、コンポーネントモデルをビューと同期させる必要があります。 ユーザーがビューを通じて値を変更したり選択したりすると、新しい値はデータモデルに反映される必要があります。 同様に、プログラムロジックがデータモデルの値を変更すると、これらの値はビューに反映される必要があります。
リアクティブフォームとテンプレート駆動フォームは、ユーザーからの変更またはプログラムによる変更からデータが流れる方法が異なります。 次の図は、上記で定義されたお気に入りの色の入力フィールドを使用して、各タイプのフォームの両方のデータフローを示しています。
リアクティブフォームのデータフロー
リアクティブフォームでは、ビュー内の各フォーム要素は、フォームモデル(FormControl インスタンス)に直接リンクされています。
ビューからモデルへの更新、およびモデルからビューへの更新は同期であり、UIのレンダリング方法に依存しません。
ビューからモデルへの図は、入力フィールドの値がビューから変更された場合にデータがどのように流れるかを示しています。
- ユーザーは入力要素にお気に入りの色 Blue を入力します。
- フォーム入力要素は、最新の値を伴う "input" イベントを発行します。
- フォーム入力要素のイベントをリッスンしている
ControlValueAccessorは、新しい値をFormControlインスタンスにすぐに中継します。 FormControlインスタンスは、valueChangesObservableを通じて新しい値を発行します。valueChangesObservableのサブスクライバーは、新しい値を受け取ります。
モデルからビューへの図は、モデルに対するプログラムによる変更がどのようにビューに伝播されるかを示しています。
- ユーザーは
favoriteColorControl.setValue()メソッドを呼び出し、FormControlの値を更新します。 FormControlインスタンスは、valueChangesObservableを通じて新しい値を発行します。valueChangesObservableのサブスクライバーは、新しい値を受け取ります。- フォーム入力要素の制御値アクセサーは、新しい値で要素を更新します。
テンプレート駆動フォームのデータフロー
テンプレート駆動フォームでは、各フォーム要素は、フォームモデルを内部的に管理するディレクティブにリンクされています。
ビューからモデルへの図は、入力フィールドの値がビューから変更された場合にデータがどのように流れるかを示しています。
- ユーザーは入力要素に Blue を入力します。
- 入力要素は、値 Blue を伴う "input" イベントを発行します。
- 入力に接続された制御値アクセサーは、
FormControlインスタンスのsetValue()メソッドをトリガーします。 FormControlインスタンスは、valueChangesObservableを通じて新しい値を発行します。valueChangesObservableのサブスクライバーは、新しい値を受け取ります。- 制御値アクセサーは、
ngModelChangeイベントを発行するNgModel.viewToModelUpdate()メソッドも呼び出します。 - コンポーネントテンプレートが
favoriteColorプロパティに対して双方向バインディングを使用しているため、コンポーネントのfavoriteColorプロパティは、ngModelChangeイベント(Blue)によって発行された値に更新されます。
モデルからビューへの図は、favoriteColor が Blue から Red に変更された場合に、データがモデルからビューにどのように流れるかを示しています。
- コンポーネントで
favoriteColor値が更新されます。 - 変更検知が開始されます。
- 変更検知中に、
NgModelディレクティブインスタンスの入力の1つが変更されたため、ngOnChangesライフサイクルフックが呼び出されます。 ngOnChanges()メソッドは、内部FormControlインスタンスの値を設定するための非同期タスクをキューに入れます。- 変更検知が完了します。
- 次のティックで、
FormControlインスタンスの値を設定するタスクが実行されます。 FormControlインスタンスは、valueChangesObservableを通じて最新の値を発行します。valueChangesObservableのサブスクライバーは、新しい値を受け取ります。- 制御値アクセサーは、ビュー内のフォーム入力要素を、最新の
favoriteColor値で更新します。
NOTE: NgModel は、値の変更が入力バインディングから発生するため、ExpressionChangedAfterItHasBeenChecked エラーを回避するために2回目の変更検知をトリガーします。
データモデルの可変性
変更追跡の方法は、アプリケーションの効率に影響を与えます。
この違いは、お気に入りの色の入力要素を使用する前の例で示されています。
- リアクティブフォームの場合、
FormControlインスタンス は、コントロールの値が更新されると常に新しい値を返します - テンプレート駆動フォームの場合、お気に入りの色のプロパティ は、常にその新しい値に変更されます
フォームバリデーション
バリデーションは、フォームのセットを管理する上で不可欠な部分です。 必須フィールドを確認しているか、外部APIに既存のユーザー名を確認しているかにかかわらず、Angularは一連の組み込みバリデーターと、カスタムバリデーターを作成する機能を提供します。
詳細については、フォームバリデーション を参照してください。
テスト
テストは、複雑なアプリケーションで重要な役割を果たします。 フォームが正しく機能することを検証する際に、より簡単なテスト戦略が役立ちます。 リアクティブフォームとテンプレート駆動フォームは、UIをレンダリングすることに対して異なるレベルの依存関係があり、フォームコントロールとフォームフィールドの変更に基づいてアサーションを実行します。 次の例は、リアクティブフォームとテンプレート駆動フォームを使用してフォームをテストするプロセスを示しています。
リアクティブフォームのテスト
リアクティブフォームは、フォームとデータモデルに同期的にアクセスできるため、比較的簡単なテスト戦略を提供し、UIをレンダリングせずにテストできます。 これらのテストでは、ステータスとデータは、変更検知サイクルと対話せずに、コントロールを通じてクエリおよび操作されます。
次のテストは、前の例にあるお気に入りの色のコンポーネントを使用して、リアクティブフォームのビューからモデルへのデータフロー、およびモデルからビューへのデータフローを確認します。
ビューからモデルへのデータフローを確認する
最初の例は、ビューからモデルへのデータフローを確認するために、次の手順を実行します。
- ビューからフォーム入力要素をクエリし、テスト用のカスタム "input" イベントを作成します。
- 入力の新しい値を Red に設定し、フォーム入力要素で "input" イベントをディスパッチします。
- コンポーネントの
favoriteColorControl値が入力からの値と一致することをアサートします。
it('should update the value of the input field', () => { const input = fixture.nativeElement.querySelector('input'); const event = createNewEvent('input'); input.value = 'Red'; input.dispatchEvent(event); expect(fixture.componentInstance.favoriteColorControl.value).toEqual('Red'); });
次の例は、モデルからビューへのデータフローを確認するために、次の手順を実行します。
favoriteColorControl(FormControlインスタンス)を使用して、新しい値を設定します。- ビューからフォーム入力要素をクエリします。
- コントロールに設定された新しい値が入力内の値と一致することをアサートします。
it('should update the value in the control', () => { component.favoriteColorControl.setValue('Blue'); const input = fixture.nativeElement.querySelector('input'); expect(input.value).toBe('Blue'); });
テンプレート駆動フォームのテスト
テンプレート駆動フォームでテストを作成するには、変更検知プロセスの詳細な知識と、各サイクルでディレクティブがどのように実行されるかを理解する必要があり、要素が適切なタイミングでクエリ、テスト、変更されることを保証します。
次のテストは、先に述べたお気に入りの色のコンポーネントを使用して、テンプレート駆動フォームのビューからモデルへのデータフロー、およびモデルからビューへのデータフローを確認します。
次のテストは、ビューからモデルへのデータフローを確認します。
it('should update the favorite color in the component', async () => { const input = fixture.nativeElement.querySelector('input'); const event = createNewEvent('input'); input.value = 'Red'; input.dispatchEvent(event); await fixture.whenStable(); expect(component.favoriteColor()).toEqual('Red'); });
ビューからモデルへのテストで実行される手順を以下に示します。
- ビューからフォーム入力要素をクエリし、テスト用のカスタム "input" イベントを作成します。
- 入力の新しい値を Red に設定し、フォーム入力要素で "input" イベントをディスパッチします。
- テストフィクスチャを通じて変更検知を実行します。
- コンポーネントの
favoriteColorプロパティ値が入力からの値と一致することをアサートします。
次のテストは、モデルからビューへのデータフローを確認します。
it('should update the favorite color on the input field', async () => { component.favoriteColor.set('Blue'); await fixture.whenStable(); const input = fixture.nativeElement.querySelector('input'); expect(input.value).toBe('Blue'); });
モデルからビューへのテストで実行される手順を以下に示します。
- コンポーネントインスタンスを使用して、
favoriteColorプロパティの値を設定します。 - テストフィクスチャを通じて変更検知を実行します。
await fixture.whenStable()を使用して次のレンダリングを待ちます。- ビューからフォーム入力要素をクエリします。
- 入力の値が、コンポーネントインスタンスの
favoriteColorプロパティの値と一致することをアサートします。
次のステップ
リアクティブフォームの詳細については、次のガイドを参照してください。
テンプレート駆動フォームの詳細については、次のガイドを参照してください。