はじめに
Angularでクロスフィールドバリデーション(複数項目にまたがるバリデーション )を使った時に、フォーム全体をエラーにする方法を説明します。
なお、クロスフィールドバリデーションでフォームのなかのある項目のみをエラーにする方法についてはこちらの記事をご参照ください。
環境
SPAフレームワークのAngular/TypeScriptを使用しています。
- Angular: 8.2.14
- Node: 12.13.1
やりたいこと
本記事では、タイトル or 本文 のどちらかの項目に文字を入力しないと保存ボタンが有効にならないバリデーションを実装します。
バリデーションの実装
HTMLテンプレート
まず、HTMLテンプレートの実装について説明します。HTMLでは、タイトル欄、本文欄、保存/更新ボタンを定義しています。
upsert-form.component.html
<form class="example-form" (ngSubmit)="onSubmit(upsertNgForm)" [formGroup]="createFormGroup" #upsertNgForm="ngForm"> <!-- タイトル欄 --> <mat-form-field class="example-full-width"> <input matInput class="title" placeholder="タイトル" formControlName="title" /> </mat-form-field> <!-- 本文欄 --> <mat-form-field class="example-full-width"> <textarea matInput cdkTextareaAutosize cdkAutosizeMinRows="10" cdkAutosizeMaxRows="30" placeholder="本文" formControlName="description"></textarea> </mat-form-field> <!-- 保存/更新ボタン --> <button type="submit" class="login-button" mat-raised-button [disabled]="!createFormGroup.valid" color="primary"> <ng-container *ngIf="selectedMemoId; else createLabel">更新</ng-container> <ng-template #createLabel>保存</ng-template> </button> </form>
ポイントとしては、[disabled]="!createFormGroup.valid"
と記載することで、FormGroup
内でバリデーションエラーが発生している時には、ボタンが非活性になるようにしているところです。
コンポーネントクラス
続いて、コンポーネントクラスの実装について説明します。コンポーネントクラスでは、フォームグループ、フォームコントロールの設定とバリデーションの設定をしています。
upsert-form.component.ts
import { Component } from '@angular/core'; import { FormGroup, FormControl, FormBuilder, NgForm } from '@angular/forms'; import { CustomValidator } from '../../validation/custom-validator'; /** * メモの新規作成・更新フォーム * * @export * @class UpsertFormComponent * @implements {OnInit} * @implements {OnChanges} */ @Component({ selector: 'app-upsert-form', templateUrl: './upsert-form.component.html', styleUrls: ['./upsert-form.component.scss'] }) export class UpsertFormComponent implements OnInit, OnChanges { // FormGroup定義 public createFormGroup: FormGroup; // タイトルフォームのコントロール定義 public titleControl: FormControl; // 本文フォームのコントロール定義 public descriptionControl: FormControl; constructor(private fb: FormBuilder) { this.createForm(); this.folderControl = this.createFormGroup.get('folder') as FormControl; this.titleControl = this.createFormGroup.get('title') as FormControl; this.descriptionControl = this.createFormGroup.get( 'description' ) as FormControl; } /** * フォーム設定の作成 * */ private createForm() { this.createFormGroup = this.fb.group( { title: ['', []], description: ['', []] }, { validators: CustomValidator.titleOrDescriptionRequired } ); } }
フォームグループとタイトルと本文のどちらかを必須にするバリデーション を設定している部分です。
this.createFormGroup = this.fb.group( { title: ['', []], description: ['', []] }, { validators: CustomValidator.titleOrDescriptionRequired } );
validators: CustomValidator.titleOrDescriptionRequired
という風に書くことで、クロスフィールドバリデーション (項目をまたがったバリデーション )を実装することができます。
カスタムバリデーター
最後にタイトルまたは本文のどちらかを入力しないとバリデーションエラーを返すバリデーターの実装について説明します。
custom-validator.ts
import { ValidationErrors, FormGroup } from '@angular/forms'; /** * 「タイトルと本文のどちらかは必須」バリデーション * * @static * @param {*} ac * @param {*} AbstractControl * @memberof CustomValidator */ public static titleOrDescriptionRequired( control: FormGroup ): ValidationErrors | null { const title = control.get('title').value; const description = control.get('description').value; return !title && !description ? { notTitleOrDescriptionRequired: true } : null; } }
タイトルと本文の値をそれぞれ取得して、どちらも未入力の場合は、{ notTitleOrDescriptionRequired: true }
を返しています。このようにすることで、ValidationErrors
を返されたFormGoupt
はinvalid
な状態になり、保存ボタンが非活性になります。