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 件のコメント:
コメントを投稿