← Angular コース
32. シグナルフォーム — form() でフォームもシグナルに
v22で正式版になったシグナルフォームは、フォームの値をシグナルのモデルとして持ち、form() でそこからフォームを組み立てる新しい方式です。signal({ email: '', password: '' }) のようにモデルを作り、form(model, schema) に渡すと、各項目に対応したフィールドのツリーができます。これまで学んだリアクティブフォーム(FormGroup / FormControl)の、シグナル時代版だと考えると分かりやすいです。
検証は form() の第2引数に渡すスキーマ関数の中で宣言します。required(path.email) や email(path.email) のように「どの項目にどの検証を効かせるか」を書くと、値が変わるたびに自動で再判定されます。テンプレートでは入力欄に [formField]="loginForm.email" をつないで双方向に同期し、loginForm.email().errors() や loginForm.invalid() でエラーや全体の妥当性をシグナルとして読み取れます。
リアクティブフォームと違い、値もエラーも妥当性もすべてシグナルなので、computedやeffectとそのまま組み合わせられるのが利点です。新規開発のフォームはシグナルフォームが第一候補になりますが、既存のリアクティブフォームも引き続き使えるので、無理に全部を書き換える必要はありません。
サンプルコード(フレームワーク環境が必要なため表示のみ)
import { Component, signal } from '@angular/core';
import { form, FormField, required, email } from '@angular/forms/signals';
@Component({
selector: 'app-login-form',
imports: [FormField],
// [formField] で入力欄とフィールドを双方向に同期する
template:
'<input type="email" [formField]="loginForm.email">' +
'@if (loginForm.email().touched() && loginForm.email().invalid()) {' +
' @for (e of loginForm.email().errors(); track e) { <p>{{ e.message }}</p> }' +
'}' +
'<button [disabled]="loginForm().invalid()">送信</button>',
})
export class LoginFormComponent {
// フォームの中身はただのシグナルのモデル
model = signal({ email: '', password: '' });
// スキーマ関数の中で、どの項目にどの検証を効かせるかを宣言する
loginForm = form(this.model, (path) => {
required(path.email, { message: 'メールは必須です' });
email(path.email, { message: 'メール形式で入力してください' });
required(path.password, { message: 'パスワードは必須です' });
});
}