今回は、独自エラーチェック(カスタムバリデーション)を作成します。
プログラムは、以前の記事で使用した「アカウント登録機能」を使用したいと思いますので、下記の記事も参照ください。
参照1:【Angular】コンポーネントの設計(画面ごとの設計)について。
参照2:【Angular】エラーメッセージの管理について考える。
(1)登録画面の実装を確認
まずは、現在の登録画面のコンポーネントを見てみます。コード量が多いので、全体はこちら(register.component.ts)で確認ください
今回カスタムバリデーションを追加するのは、こちらの携帯番号(mobilePhoneNumber)部分です。
mobilePhoneNumberMaxLength = 11; nameMaxLength = 20; // (中略) formRegister: FormGroup = this.formBuilder.group({ mobilePhoneNumber: ['',[Validators.required,Validators.maxLength(this.mobilePhoneNumberMaxLength)]], name: ['', [Validators.maxLength(this.nameMaxLength)]], });
しかし、これだけだと、「18011112222」のような「0」始まりではない番号はエラーになりません。
このチェックを独自で作成しようと思いますが、
その前に、一応テンプレート側の記載も見ておこうと思います。
こちらもコード量が多いので、全体はこちら(register.component.html)で確認ください
下記に、今回関係する部分だけ記載しておきます。
<div> <label>携帯電話番号<span>必須</span></label> <input formControlName="mobilePhoneNumber" type="tel" inputmode="numeric" class="form-control" placeholder="携帯電話番号を入力" [ngClass]="{'alert-danger' : v.mobilePhoneNumberInvalid}" autofocus> <ng-container *ngIf="v.mobilePhoneNumberInvalid"> <p *ngIf="v.mobilePhoneNumberHasErrorRequired" class="error-message">{{ message('msg_error_field_required', '携帯電話番号') }}</p> <p *ngIf="v.mobilePhoneNumberHasErrorMaxLength" class="error-message">{{ message('msg_error_field_max', '携帯電話番号', mobilePhoneNumberMaxLength) }}</p> </ng-container> </div>
(2)カスタムバリデーションを作成する
弊社のプロジェクトでは、「app」配下に「shared」と言うディレクトリを作り、その中にプロジェクトで共通的に使うものを配置しています。今回のカスタムバリデーションも、共通的に使われるものなので、、以下のように作成します。
src/ └ app/ └ shared/ └ validator/ └ custom-validators.ts
では、携帯電話番号のチェックを実装しようと思います。
[custom-validators.ts]
import { FormControl } from '@angular/forms'; export class CustomValidators { /** * 携帯電話番号かどうか判定 * @param control Formのコントロール */ static mobilePhoneNumberValidator(control: FormControl) { const dateObj = control.value; // 当メソッドでは、電話番号は空文字で登録することも許可する if (dateObj === '') { return null; } const regexp = new RegExp('^(0{1}\\d{10})'); if ( typeof dateObj === 'undefined' || dateObj === null || !regexp.test(dateObj) ) { return { mobilePhoneNumberFormat: true }; } return null; } }
「当メソッドでは、電話番号は空文字で登録することも許可する」と言うコメントの部分が特殊なのですが、例えば、「任意の項目」でこのバリデーションを使いたくなった場合、そのまま使うと「未入力」でもエラーになってしまうので、未入力はエラーとしないようにしています。
どっちにしても、必須かどうかは、通常のバリデーションで行なっているので、そちらに任せる、と言う感じです。
それと、「携帯電話番号かどうか」はかなり適当に記載していますので、ご了承ください(今回は、その説明が本質では無いので)
ちょっと分かり難い(勘違いしやすい)のですが、「エラーになるパターンは『true』を返却し、エラーとしない場合『null』を返却しています」
(3)作成したカスタムバリデーションを使う
では、作成したカスタムバリデーションを実際に使ってみます。「register.component.ts」に実装していきますが、まずは先ほどのカスタムバリデーションをimportします。
import { CustomValidators } from '@app/shared/validator/custom-validators';
importしたカスタムバリデーションは、通常のバリデーションと同じ流れで定義します。
formRegister: FormGroup = this.formBuilder.group({ mobilePhoneNumber: [ '', [ Validators.required, // ←通常のバリデーション Validators.maxLength(this.mobilePhoneNumberMaxLength), // ←通常のバリデーション CustomValidators.mobilePhoneNumberValidator, // ←カスタムバリデーション ], ], name: ['', [Validators.maxLength(this.nameMaxLength)]], });
とても簡単ですね、次は、別ファイルにしている「register.validator.ts」にも追記します。
こちらは、全文そのまま記載しようと思います。
[register.component.html]
import { Injectable } from '@angular/core'; import { FormGroup } from '@angular/forms'; @Injectable() export class RegisterValidator { private form: FormGroup; constructor() {} set formGroup(form: FormGroup) { this.form = form; } get mobilePhoneNumberInvalid() { return ( this.form.controls['mobilePhoneNumber'].invalid && (this.form.controls['mobilePhoneNumber'].dirty || this.form.controls['mobilePhoneNumber'].touched) ); } get mobilePhoneNumberHasErrorRequired() { return this.form.controls['mobilePhoneNumber'].hasError('required'); } get mobilePhoneNumberHasErrorMaxLength() { return ( !this.form.controls['mobilePhoneNumber'].hasError('required') && this.form.controls['mobilePhoneNumber'].hasError('maxlength') ); } get mobilePhoneNumberHasErrorFormat() { return ( !this.form.controls['mobilePhoneNumber'].hasError('required') && this.form.controls['mobilePhoneNumber'].hasError( 'mobilePhoneNumberFormat', ) ); } get nameInvalid() { return ( this.form.controls['name'].invalid && (this.form.controls['name'].dirty || this.form.controls['name'].touched) ); } get nameHasErrorMaxLength() { return this.form.controls['name'].hasError('maxlength'); } }
追記した部分は以下の通りです。
get mobilePhoneNumberHasErrorFormat() { return ( !this.form.controls['mobilePhoneNumber'].hasError('required') && this.form.controls['mobilePhoneNumber'].hasError( 'mobilePhoneNumberFormat', ) ); }この「mobilePhoneNumberHasErrorFormat」は、テンプレート側で使うことになります。
では、そのテンプレート側も変更しようと思います。
<div> <label>携帯電話番号<span>必須</span></label> <input formControlName="mobilePhoneNumber" type="tel" inputmode="numeric" class="form-control" placeholder="携帯電話番号を入力" [ngClass]="{'alert-danger' : v.mobilePhoneNumberInvalid}" autofocus> <ng-container *ngIf="v.mobilePhoneNumberInvalid"> <p *ngIf="v.mobilePhoneNumberHasErrorRequired" class="error-message">{{ message('msg_error_field_required', '携帯電話番号') }}</p> <p *ngIf="v.mobilePhoneNumberHasErrorMaxLength" class="error-message">{{ message('msg_error_field_max', '携帯電話番号', mobilePhoneNumberMaxLength) }}</p> <p *ngIf="v.mobilePhoneNumberHasErrorFormat" class="error-message">{{ message('msg_error_cellphone_number') }}</p> </ng-container> </div>
下記が追加した部分です。今回、新たにエラーメッセージも追加しています。
(エラーメッセージの管理については、「【Angular】エラーメッセージの管理について考える。」を参照ください)
<p *ngIf="v.mobilePhoneNumberHasErrorFormat" class="error-message">{{ message('msg_error_cellphone_number') }}</p>
これで、独自エラーチェック(カスタムバリデーション)を実装できました。
ある程度規模の大きいプロジェクトになると、結構な数のカスタムバリデーションを作る事になるかと思いますので、参考にして頂ければ幸いです。
Angular