Dev Study
← 解説「フォームのバリデーション — Validators」に戻る

サンプルコードで身につける: フォームのバリデーション — Validators

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1Validators.required — 必須入力にする

FormControl の第2引数に Validators.required を渡すと、未入力の間はそのコントロールが invalid になります。form.invalid を送信ボタンの [disabled] につなぐだけで、「入力するまで送信できない」挙動が作れます。もっとも基本で、もっとも使用頻度の高いバリデーターです。

import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';

@Component({
  selector: 'app-name-form',
  imports: [ReactiveFormsModule],
  // 未入力の間は form.invalid が true なのでボタンが押せない
  template:
    '<form [formGroup]="form">' +
    '  <input formControlName="name" placeholder="名前(必須)">' +
    '  <button [disabled]="form.invalid">送信</button>' +
    '</form>',
})
export class NameFormComponent {
  form = new FormGroup({
    // 第2引数にバリデーターを渡す
    name: new FormControl('', Validators.required),
  });
}

2Validators.email — メール形式を検証する

Validators.email を付けると、値がメールアドレスの形式かどうかを判定してくれます。形式が正しくない間は invalid になり、errors に email のエラーが入ります。注意点として、空文字はこのバリデーターではエラーにならないため、必須にしたい場合は required と組み合わせます。

import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';

@Component({
  selector: 'app-email-form',
  imports: [ReactiveFormsModule],
  template:
    '<form [formGroup]="form">' +
    '  <input formControlName="email" placeholder="メールアドレス">' +
    '  @if (form.get("email")?.invalid) {' +
    '    <p>メール形式で入力してください</p>' +
    '  }' +
    '</form>',
})
export class EmailFormComponent {
  form = new FormGroup({
    // 'tanaka' は invalid、'tanaka@example.com' は valid になる
    email: new FormControl('', Validators.email),
  });
}

3Validators.minLength — 最低文字数を決める

Validators.minLength(8) のように数を渡すと、その文字数未満の間は invalid になります。パスワードやユーザー名など「短すぎる入力を弾きたい」項目の定番です。errors の minlength には要求文字数と実際の文字数が入るので、メッセージに埋め込むこともできます。

import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';

@Component({
  selector: 'app-password-form',
  imports: [ReactiveFormsModule],
  template:
    '<form [formGroup]="form">' +
    '  <input formControlName="password" type="password">' +
    '  @if (form.get("password")?.errors?.["minlength"]) {' +
    '    <p>パスワードは8文字以上にしてください</p>' +
    '  }' +
    '</form>',
})
export class PasswordFormComponent {
  form = new FormGroup({
    // 8文字未満の間は errors に minlength が入る
    password: new FormControl('', Validators.minLength(8)),
  });
}

4複数のバリデーターを配列で組み合わせる

1つの項目に複数の条件を課すときは、バリデーターを配列で渡します。[Validators.required, Validators.email] なら「必須かつメール形式」で、どちらか1つでも失敗すればそのコントロールは invalid です。実務のフォームでは、ほとんどの項目がこの組み合わせ形になります。

import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';

@Component({
  selector: 'app-signup-form',
  imports: [ReactiveFormsModule],
  template:
    '<form [formGroup]="form">' +
    '  <input formControlName="email" placeholder="メール">' +
    '  <input formControlName="password" type="password">' +
    '  <button [disabled]="form.invalid">登録</button>' +
    '</form>',
})
export class SignupFormComponent {
  form = new FormGroup({
    // 配列で渡すと「すべて満たしたときだけ valid」になる
    email: new FormControl('', [Validators.required, Validators.email]),
    password: new FormControl('', [Validators.required, Validators.minLength(8)]),
  });
}

5errors と touched でエラーメッセージを出し分ける

errors にはどの検証で失敗したかが入るため、required と email で別々のメッセージを出せます。さらに touched(一度触れたか)と組み合わせて「触れたあとに無効ならエラー表示」とすると、入力前から赤いエラーが出る不自然さを防げます。実務のエラー表示は、ほぼこのパターンに落ち着きます。

import { Component } from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';

@Component({
  selector: 'app-email-errors',
  imports: [ReactiveFormsModule],
  // touched && invalid のときだけ、失敗した検証に応じたメッセージを出す
  template:
    '<form [formGroup]="form">' +
    '  <input formControlName="email" placeholder="メール">' +
    '  @if (email.touched && email.invalid) {' +
    '    @if (email.errors?.["required"]) { <p>メールは必須です</p> }' +
    '    @if (email.errors?.["email"]) { <p>メール形式で入力してください</p> }' +
    '  }' +
    '</form>',
})
export class EmailErrorsComponent {
  email = new FormControl('', [Validators.required, Validators.email]);
  form = new FormGroup({ email: this.email });
}