入門
基本概念

シグナルを使ったフォーム

シグナルフォームはAngularのシグナルを使用してフォームの状態を管理し、AngularのシグナルでデータモデルとUI間の自動的な同期を提供します。

このガイドでは、シグナルフォームでフォームを作成するための中心的な概念を順を追って説明します。その仕組みは次のとおりです:

最初のフォームを作成する

1. signal()でフォームモデルを作成する

すべてのフォームは、フォームのデータモデルを保持するシグナルを作成することから始まります:

interface LoginData {
  email: string;
  password: string;
}

const loginModel = signal<LoginData>({
  email: '',
  password: '',
});

2. フォームモデルをform()に渡してFieldTreeを作成する

次に、フォームモデルをform()関数に渡してフィールドツリーを作成します。これはモデルの形状を反映したオブジェクト構造で、ドット記法でフィールドにアクセスできます。

Both the root form object and its nested properties are FieldTree nodes:

const loginForm = form(loginModel);

loginForm; // is a FieldTree
loginForm.email; // is also a FieldTree

3. [formField]ディレクティブでHTML入力をバインドする

次に、[formField]ディレクティブを使用してHTMLの入力をフォームにバインドします。これにより、それらの間に双方向バインディングが作成されます:

<input type="email" [formField]="loginForm.email" />
<input type="password" [formField]="loginForm.password" />

その結果、ユーザーによる変更(フィールドへの入力など)は自動的にフォームを更新します。

NOTE: [formField]ディレクティブは、必要に応じてrequireddisabledreadonlyなどの属性のフィールドの状態も同期します。

4. FieldTreeシグナルで状態を読み取る

ツリーの任意の部分の状態には、FieldTreeノードを関数として呼び出すことでアクセスできます。これにより、値、バリデーションステータス、インタラクションの状態に対するリアクティブなシグナルを含む状態オブジェクトが返されます:

loginForm(); // Returns state for the whole form
loginForm.email(); // Returns state for the email field

フィールドの現在の値を読み取るには、value()シグナルにアクセスします:

<!-- Render values that update automatically as user types -->
<p>Form value: {{ loginForm().value() | json }}</p>
<p>Email: {{ loginForm.email().value() }}</p>
// Get the current value
const currentEmail = loginForm.email().value();

5. set()で値を更新する

任意のノードでvalue.set()メソッドを使用して、プログラムから値を更新できます。これにより、FieldTreeと基になるモデルシグナルの両方が更新されます:

// Update the value programmatically
loginForm.email().value.set('[email protected]');

その結果、フィールドの値とモデルシグナルの両方が自動的に更新されます:

// The model signal is also updated
console.log(loginModel().email); // '[email protected]'

Complete example

基本的な使い方

[formField]ディレクティブは、すべての標準的なHTMLのinputタイプで動作します。以下は、最も一般的なパターンです:

テキスト入力

テキスト入力は、さまざまなtype属性やtextareaで動作します:

<!-- Text and email -->
<input type="text" [formField]="form.name" />
<input type="email" [formField]="form.email" />

数値

数値入力は、文字列と数値を自動的に変換します:

<!-- Number - automatically converts to number type -->
<input type="number" [formField]="form.age" />

日付と時刻

日付入力は値をYYYY-MM-DD形式の文字列として保存し、時刻入力はHH:mm形式を使用します:

<!-- Date and time - stores as ISO format strings -->
<input type="date" [formField]="form.eventDate" />
<input type="time" [formField]="form.eventTime" />

日付文字列をDateオブジェクトに変換する必要がある場合は、フィールドの値をDate()に渡すことで変換できます:

const dateObject = new Date(form.eventDate().value());

複数行テキスト

Textareaはテキスト入力と同じように動作します:

<!-- Textarea -->
<textarea [formField]="form.message" rows="4"></textarea>

チェックボックス

チェックボックスはブール値にバインドされます:

<!-- Single checkbox -->
<label>
  <input type="checkbox" [formField]="form.agreeToTerms" />
  I agree to the terms
</label>

複数チェックボックス

複数のオプションがある場合は、それぞれに個別のブール値のformFieldを作成します:

<label>
  <input type="checkbox" [formField]="form.emailNotifications" />
  Email notifications
</label>
<label>
  <input type="checkbox" [formField]="form.smsNotifications" />
  SMS notifications
</label>

ラジオボタン

ラジオボタンはチェックボックスと同様に動作します。ラジオボタンが同じ[formField]値を使用している限り、シグナルフォームは自動的に同じname属性をすべてのラジオボタンにバインドします:

<label>
  <input type="radio" value="free" [formField]="form.plan" />
  Free
</label>
<label>
  <input type="radio" value="premium" [formField]="form.plan" />
  Premium
</label>

ユーザーがラジオボタンを選択すると、フォームのformFieldにはそのラジオボタンのvalue属性の値が保存されます。例えば、「Premium」を選択すると、form.plan().value()"premium"に設定されます。

selectドロップダウン

Select要素は、静的オプションと動的オプションの両方で動作します:

<!-- Static options -->
<select [formField]="form.country">
  <option value="">Select a country</option>
  <option value="us">United States</option>
  <option value="ca">Canada</option>
</select>

<!-- Dynamic options with @for -->
<select [formField]="form.productId">
  <option value="">Select a product</option>
  @for (product of products; track product.id) {
    <option [value]="product.id">{{ product.name }}</option>
  }
</select>

NOTE: 複数選択(<select multiple>)は、現時点では[formField]ディレクティブでサポートされていません。

バリデーションと状態

シグナルフォームには、フォームフィールドに適用できる組み込みのバリデーターが用意されています。バリデーションを追加するには、form()の第2引数にスキーマ関数を渡します:

const loginForm = form(loginModel, (schemaPath) => {
  debounce(schemaPath.email, 500);
  required(schemaPath.email);
  email(schemaPath.email);
});

スキーマ関数は、バリデーションルールを設定するためのフィールドへのパスを提供するスキーマパスパラメーターを受け取ります。

一般的なバリデーターには次のものがあります:

  • required() - フィールドに値があることを保証します
  • email() - メール形式を検証します
  • min() / max() - 数値の範囲を検証します
  • minLength() / maxLength() - 文字列またはコレクションの長さを検証します
  • pattern() - 正規表現パターンに対して検証します

バリデーターの第2引数にオプションオブジェクトを渡すことで、エラーメッセージをカスタマイズできます:

required(schemaPath.email, {message: 'Email is required'});
email(schemaPath.email, {message: 'Please enter a valid email address'});

FieldTreeのノードは、リアクティブなシグナルを通じてそのバリデーションおよびインタラクションの状態を公開します。

FieldTreeの状態シグナル

Every node in the tree, including the root form object, provides the same signals to track its state. Since every node is a FieldTree, the API for monitoring validity and interaction is identical at every level.

State Description
valid() Returns true if the node passes all validation rules
invalid() Returns true if there are validation errors
pending() Returns true if async validation is in progress
touched() Returns true if the user has focused and blurred the field or any child field
dirty() Returns true if the value has been changed by the user
disabled() Returns true if the node is disabled
readonly() Returns true if the node is readonly
errors() Returns an array of validation errors with kind and message properties

Complete example

次のステップ

シグナルフォームとその仕組みについてさらに詳しく学ぶには、詳細なガイドをご覧ください:

  • 概要 - シグナルフォームの紹介といつ使用するか
  • フォームモデル - シグナルを使用したフォームデータの作成と管理
  • フィールドの状態管理 - バリデーション状態、インタラクションの追跡、フィールドの可視性の操作
  • バリデーション - 組み込みバリデーター、カスタムバリデーションルール、非同期バリデーション