【Angular】ReactiveFormsの使い方!FormControlでフォームを作ろう!

Angularには、フォームを作成する方法として「テンプレート駆動フォーム(テンプレート駆動型)」と「リアクティブフォーム(モデル駆動型)」の2種類があります。

この記事では、リアクティブフォームの中でも最も基本的な構成であるFormControlの使い方について、サンプルコードを用いてわかりやすく解説します。

【ReactiveForms】FormControlの使い方

準備:ReactiveFormsModuleをインポートする

まず、Angularでリアクティブフォームを使うには、ReactiveFormsModuleをアプリケーションに読み込む必要があります。そのため、まずapp.module.tsReactiveFormsModuleを追加しましょう。

// src/app/app.module.ts
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    ReactiveFormsModule, // ← リアクティブフォーム機能を使うために必要!
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

ReactiveFormsModuleをインポートすると、[formControl]ディレクティブなど、リアクティブフォームで使う機能が利用できるようになります。

FormControlを定義する

フォームの入力欄を制御するためには、FormControlを使います。以下に実装例を示します。

// src/app/app.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
    <!-- nameというフォームコントロールをinputタグと連携 -->
    <input type="text" [formControl]="name" />

    <!-- フォームコントロール値の表示 -->
    <p>現在の値:{{ name.value }}</p>

    <!-- フォームコントロール値の置換 -->
    <button type="button" (click)="updateName()">名前を「Taro」に更新</button>
  `,
})
export class AppComponent {
  name = new FormControl(''); // ← 初期値は空文字

  updateName() {
    this.name.setValue('Taro');
  }
}
  • name = new FormControl('');
    • フォームコントロールのインスタンスnameを作成しています。初期値は空の文字列('')です。
  • <input type="text" [formControl]="name" />
    • nameというフォームコントロールとinputタグをバインドしています。これで双方向バインディングが自動で行われます。

フォームコントロール値の表示方法

フォームに入力した値をリアルタイムで表示することもできます。以下のコードでは、name.valueを使って、現在の入力値を表示しています。入力フィールドに文字を入力すると、入力された値がリアルタイムに反映されます。

<p>現在の値:{{ name.value }}</p>

フォームコントロール値の置換方法(setValue)

プログラムからフォームコントロールの値を更新するには、setValue()メソッドを使います。以下のサンプルコードのように実装した場合、ボタンをクリックすると、updateName()が実行され、nameの値が'Taro'に置き換えられます。

<button type="button" (click)="updateName()">名前を「Taro」に更新</button>
// src/app/app.component.ts
updateName() {
  this.name.setValue('Taro');
}

【補足】バリデーションの方法

バリデーションを設定したい場合は、FormControlの第2引数にバリデーション関数を渡します。

// src/app/app.component.ts
import { Component } from '@angular/core';
import {
  AbstractControl,
  FormControl,
  ValidationErrors,
  ValidatorFn,
  Validators,
} from '@angular/forms';

@Component({
  selector: 'app-root',
  template: `
    <!-- nameというフォームコントロールをinputタグと連携 -->
    <input type="text" [formControl]="name" />

    <!-- エラーメッセージの表示 -->
    <div *ngIf="name.invalid && (name.dirty || name.touched)">
      <div *ngIf="name.hasError('required')">名前は必須です。</div>
      <div *ngIf="name.hasError('minlength')">
        名前は4文字以上で入力してください。
      </div>
      <div *ngIf="name.hasError('forbiddenName')">
        「Bob」という名前は禁止されています。
      </div>
    </div>
  `,
})
export class AppComponent {
  // バリデーション付きのFormControl
  name = new FormControl('', [
    Validators.required, // 入力必須
    Validators.minLength(4), // 4文字以上
    this.forbiddenNameValidator(/^bob$/i), // 「Bob」は禁止
  ]);

  // カスタムバリデーターの定義
  forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      const forbidden = nameRe.test(control.value);
      return forbidden ? { forbiddenName: { value: control.value } } : null;
    };
  }
}

Angularのリアクティブフォームでは、以下のような組み込みバリデータが用意されています。

バリデーション内容
Validators.required入力必須
Validators.minLength(n)最小文字数n以上
Validators.maxLength(n)最大文字数n以下
Validators.emailメール形式であること
Validators.pattern()正規表現にマッチすること

また、カスタムバリデータも作成することができます。関数forbiddenNameValidator()は、FormControlの値が特定の文字列(この場合 "bob")に一致した場合にエラーを返します。正規表現/^bob$/iで大文字小文字を区別せずに判定しています。

エラーメッセージについて

<div *ngIf="name.invalid && (name.dirty || name.touched)">

上記のコードでは、次のような場合にだけエラーが表示されるようにしています。

  • invalid
    • バリデーションエラーがある場合
  • dirty or touched
    • ユーザーが入力欄を操作済みの場合
      • dirty:値が変更された状態である場合
      • touched:フォーカスが当たった後に離れた状態である場合
  • hasError()
    • name.hasError('required'): 入力必須チェックエラー(空文字だった場合)
    • name.hasError('minlength'): 最小文字数エラー
    • name.hasError('forbiddenName'): カスタムバリデーションエラー

【補足】FormGroupやFormArrayの活用も!

今回はリアクティブフォームの基本であるFormControlの使い方を解説しましたが、実際のフォームでは複数の入力欄をまとめて管理したくなることも多いです。そんなときは、FormGroupFormArrayといったクラスを使うことで、複数のFormControlをグループ化して、より複雑なフォームを簡単に扱えるようになります。

FormGroup の簡単な例

this.profileForm = new FormGroup({
  firstName: new FormControl(''),
  lastName: new FormControl(''),
});
<form [formGroup]="profileForm">
  <input type="text" formControlName="firstName" />
  <input type="text" formControlName="lastName" />
</form>

今後、フォームを扱う機会が増えてきたら、ぜひFormGroupFormArrayFormBuilderなどの機能も学んでみてください!

本記事のまとめ

この記事では、Angularのリアクティブフォームの基本であるFormControlの使い方について、以下の内容を説明しました。

  • ReactiveFormsModuleのインポート方法
  • FormControlの定義とバインディング方法
  • 入力値の表示方法と更新方法
  • バリデーションの設定方法

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