Angularでリアルタイムバリデーションを実装する

目次

前置き

クライアント側での単項目チェックによく使われる「リアルタイムバリデーション」をAngularで実装するためのTIPSです。

実装手順

フォームグループを作る

  • FormBuilderFormGroup を使ってコントロールをまとめる
form = this.fb.group({
  username: ['', []],
  email: ['', []]
});

バリデーションを設定する

  • 各コントロールに Validators を追加
  • 非同期バリデーション(サーバー照会)を使うなら asyncValidators に設定
form = this.fb.group({
  username: ['', [Validators.required, Validators.minLength(3)]],
  email: ['', [Validators.required, Validators.email]]
});

HTMLにフォームを描く

  • formGroup ディレクティブでフォーム全体をバインド
  • formControlName 属性で各入力とコントロールを紐づける
  • エラーメッセージは control.touched && control.invalid を条件に表示

Angular 15 までの書き方

  • 構文は *ngIf を使う
<form [formGroup]="form">
  <input formControlName="username">
  <div *ngIf="form.get('username')?.touched && form.get('username')?.invalid">
    ユーザー名が不正です
  </div>
</form>

Angular 17 以降の書き方

  • 新しい構文ベースの制御フローが追加され、@if / @for / @switch が使える
  • ngIf でも書けるけど、17以降はこの構文が公式に推奨されている
<form [formGroup]="form">
  <input formControlName="username">

  @if (username.touched && username.invalid) {
    <p class="error">ユーザー名が不正です</p>
  }
</form>

イメージ

ヘルパについて

Angularのフォームまわりでよく出てくる 「繰り返し書きがちな処理をまとめた補助関数」 のことをヘルパと呼びます。

バリデーションやエラーメッセージ周りは、どうしてもテンプレートに条件式が増えがちです。

そこで「処理を関数化」してまとめると効率が良くなります。

ヘルパの例

コントロール取得ヘルパ

getCtl(name: string) {
  return this.form.get(name);
}

→ HTMLで form.get('username') を毎回書かなくて済む

@if (getCtl('username')?.touched && getCtl('username')?.invalid) {
  <p class="error">ユーザー名が不正です</p>
}

エラーメッセージ生成ヘルパ

getErrorMessage(controlName: string): string | null {
  const c = this.form.get(controlName);
  if (!c || !c.errors) return null;

  if (c.errors['required']) return '必須項目です。';
  if (c.errors['minlength']) return `最低 ${c.errors['minlength'].requiredLength} 文字必要です。`;
  if (c.errors['email']) return 'メール形式が不正です。';

  return '入力内容を確認してください。';
}

→ HTMLがスッキリ書ける

@if (username.touched && username.invalid) {
  <p class="error">{{ getErrorMessage('username') }}</p>
}

共通バリデータを作るヘルパ

コンポーネント外(validators/ ディレクトリなどに共通バリデーションモジュールを配置)にまとめることも多いです。

import { AbstractControl, ValidationErrors } from '@angular/forms';

export function noEmojiValidator(c: AbstractControl): ValidationErrors | null {
  const regex = /[\\u{1F600}-\\u{1F64F}]/u; // 絵文字
  return regex.test(c.value) ? { noEmoji: true } : null;
}

→ 複数のフォームで再利用できる。

ヘルパのメリット

  • テンプレートが読みやすくなる
  • 同じロジックを何度も書かなくて済む
  • 1入力項目に複数エラーが出たときも「最初の1つだけ表示」など、ルールを一元化できる
  • プロジェクトが大きくなっても保守が楽

だいたいの場合、バリデーションを実装するときは、ヘルパもセットになるはず!

最後に

ざっくりではありますが、それなりにまとまったような気がしています。しかし、まだ慣れないなあ、Angularには。

やまぐろ
この記事を書いた人
業務アプリケーション開発、エンドユーザ向け機能などの開発に携わっている文系(経営学)卒エンジニア。
当サイトでは読書記録を残したり、ガジェットのレビューをしたりしています。
たまにエンジニアっぽい記事を書いたりすることも。

コメント

コメントする

このサイトは reCAPTCHA によって保護されており、Google のプライバシーポリシー および 利用規約 に適用されます。

reCaptcha の認証期間が終了しました。ページを再読み込みしてください。

目次