Angularに限ったことでは無いですが、Form の Validation って、記載が煩雑になりますよね。
弊社内のAngularプロジェクトでは、出来る限り記載を楽にしようと日々試行錯誤しています。
今回は、本家サイトのValidationの説明を参考に、
入力必須で最大10文字の「名前」項目
を作ってみます。
(1)とりあえずバリデーションを作ってみる
まずはテンプレート側を作ってみます。[register.component.html]
<form [formGroup]="formRegister" (ngSubmit)="onSubmit()" (keydown.enter)="$event.preventDefault()"> <div> <label>お名前</label> <input formControlName="name" type="text" class="form-control" placeholder="お名前を入力(必須)"> <ng-container *ngIf="name.invalid && (name.dirty || name.touched)"> <ng-container *ngIf="name.errors?.['required']"> <p class="error-message">お名前は必ず入力してください。</p> </ng-container> <ng-container *ngIf="name.errors?.['maxLength']"> <p class="error-message">お名前は10文字以内で入力してください。</p> </ng-container> </ng-container> </div> <button [disabled]="formRegister.invalid" type="submit">登録</button> </form>
続いて、コントロール(コンポーネント)側を作ります。(抜粋)
[register.component.ts]
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; // (中略) export class RegisterComponent implements OnInit { // (中略) formRegister: FormGroup = this.formBuilder.group({ name: ['', [Validators.required, Validators.maxLength(10)]], }); constructor( private formBuilder: FormBuilder, ) {} get name() { return this.formRegister.get('name'); } ngOnInit(): void { // (後略)
ここだけ見ると、何て簡単なんだ!って思うのですが・・・・・
(2)エラー時に背景色を変える
では、ここで、バリデーションエラーの時に、input項目の背景色を変えたい、となったとします。テンプレート側の修正をしましょう。
[register.component.html]
<label>お名前</label> <input formControlName="name" type="text" class="form-control" placeholder="お名前を入力(必須)" [ngClass]="{'alert-danger' : name.invalid && (name.dirty || name.touched)}">「ngClass」を追加しました。
※ngClassの利用方法については、以前の記事を参照ください。
「name.invalid && (name.dirty || name.touched)」の部分、
同じ条件なのに、また記載する事に・・・・
例えば、「名前」の他に、「住所」、「電話番号」などが増えていくと、
その都度、記載していく事に・・・・テンプレートがゴチャゴチャしていきますね。
特に、「 (xxxx.dirty || xxxx.touched)」の記載が大量になります。
「デザイナーとの分業?知ったこっちゃねぇ」なら良いですが、普通は分業していると思うのでhtml(デザイン)に修正が入ると、どんどんAngularに取り込むのが辛くなります。
「関心の分離」と言うことで、バリデーションはバリデーション専用のクラスを作り、それをコントロール(コンポーネント)側にDIする形にしてみようと思います。
(3)バリデーションの処理を分ける
まずは、バリデーション専用のクラスを新規で作成します。[register.validator.ts]
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 nameInvalid() { return ( this.form.controls['name'].invalid && (this.form.controls['name'].dirty || this.form.controls['name'].touched) ); } get nameHasErrorRequired() { return this.form.controls['name'].hasError('required'); } get nameHasErrorMaxLength() { return this.form.controls['name'].hasError('maxlength'); } }上記クラスは、モジュールで定義をしてDI出来るようにしておいてください
@NgModule({ providers: [ RegisterValidator, ],
続いて、コントロール(コンポーネント)側でDIします。
[register.component.ts]
import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { RegisterValidator } from './register.validator'; // (中略) export class RegisterComponent implements OnInit { // (中略) formRegister: FormGroup = this.formBuilder.group({ name: ['', [Validators.required, Validators.maxLength(10)]], }); constructor( private formBuilder: FormBuilder, public v: RegisterValidator, ) {} ngOnInit(): void { // バリデーション設定 this.v.formGroup = this.formRegister; // (後略)
get name() { return this.formRegister.get('name'); }
は不要になるので、削除しています。
コンストラクタで、
constructor( private formBuilder: FormBuilder, public v: RegisterValidator, ) {}のように定義して、DIを行います。 public にしているのは、このオブジェクトをテンプレート側で使いたい為です。
また、変数名はなるべく短い方が、テンプレート側がゴチャゴチャしません
「ngOnInit」で、Formの設定内容をバリデーションクラスに送るのも忘れずに。
ngOnInit(): void { // バリデーション設定 this.v.formGroup = this.formRegister;
(4)テンプレート側の記載を変える
最後はテンプレート側を変更します。[register.component.html]
<form [formGroup]="formRegister" (ngSubmit)="onSubmit()" (keydown.enter)="$event.preventDefault()"> <div> <label>お名前</label> <input formControlName="name" type="text" class="form-control" placeholder="お名前を入力(必須)" [ngClass]="{'alert-danger' : v.nameInvalid}"> <ng-container *ngIf="v.nameInvalid"> <ng-container *ngIf="v.nameHasErrorRequired"> <p class="error-message">お名前は必ず入力してください。</p> </ng-container> <ng-container *ngIf="v.nameHasErrorMaxLength"> <p class="error-message">お名前は10文字以内で入力してください。</p> </ng-container> </ng-container> </div> <button class="btn btn1 ml-auto" [disabled]="formRegister.invalid" type="submit">登録</button> </form>
「name.invalid && (name.dirty || name.touched)」の条件を「v.nameInvalid」で判断出来るので、かなり記載がスッキリします。
「v.nameHasErrorRequired」などについても、実際の処理は、バリデーション専用のクラス側にあるので、処理の分離も出来ています。
という事で、今回はAngularのValidationの書き方を簡略化してみました。
Angular
0 件のコメント:
コメントを投稿