今回はtemplate drivenでのフォーム作成、validationまでやってみます
angular4系で動作させてます
template driven form
とりあえずテキストフィールド一つ作ります
- app.component.ts
import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class ValidationComponent { public model: any = {}; constructor() { } public onSubmit(){ console.log(this.model); } }
- app.component.html
<div style="width: 80%; padding: 20px;"> <form #f="ngForm" (submit)="onSubmit()"> <div class="form-group"> <label for="name">Name</label> <input type="text" name="name" id="name" [(ngModel)]="model.name" class="form-control" #name="ngModel" required> <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger"> <div *ngIf="name.errors.required"> Name is required </div> </div> </div> <button type="submit" [disabled]="!f.form.valid" class="btn btn-sm btn-primary">Submit</button> </form> </div>
#name="ngModel" [(ngModel)]="model.name"
model.nameをこのフォームと関連付ける
この手法なら今は便宜上anyにしてますがちゃんと型定義すればわかりやすそうですね
#name
でテンプレート変数化しているのはテンプレート側でvalidationの結果を使ってアラートを表示したりするのに使用するため
style
デフォルトで状況に応じて下記のクラスが適用されるので状況に合わせたクラスのスタイルを定義してあげるだけでそれなりに見れる、楽
クラス名 | 状態 |
---|---|
.ng-valid | validationに引っかかっていない状態 |
.ng-invalid | validationに引っかかっている状態 |
.ng-pending | pending中…? |
.ng-pristine | 変更されていない |
.ng-dirty | 変更されている |
.ng-untouched | toucheされてない |
.ng-touched | toucheされた |
- 参照
https://angular.io/guide/form-validationangular.io
- app.component.scss
.ng-invalid:not(form) { border-left: 3px solid #CC3333; border-radius: 3px; }
公式にも乗っていますがこんな感じでnameが空の時に左側が赤くなる感じですね
custom validation(directive)
次はディレクティブを用意してinput要素に適用することでカスタムvalidatorを実装してみます
ng g validators/name-validator
でテンプレートが自動生成されるので必要なところを記述していきます
- app/validators/name-validator.directive.ts
import { Directive,Input } from '@angular/core'; import {Validator, AbstractControl, NG_VALIDATORS} from '@angular/forms'; @Directive({ selector: '[nameValidator]', providers: [{provide: NG_VALIDATORS, useExisting: NameValidatorDirective, multi: true}] }) export class NameValidatorDirective implements Validator { constructor() { } @Input() banName: string; validate(control: AbstractControl): {[key: string]: any}{ return (this.banName == control.value) ? {banName: {value: control.value}} : null; } }
Validator
をimplementsしているのでvalidate
関数を使います、返り値はnullか任意の key: value の値です
今回は引数で受け取るbanName
に一致する名前かどうかを見て一致した場合はエラーとします
- component.html
テンプレート側でdirectiveを適用します
directiveのselector nameValidator
をinput要素に適用させてあげます
今回は引数としてbanName
を渡してます
<div class="form-group"> <label for="name">Name</label> <input type="text" name="name" id="name" [(ngModel)]="model.name" class="form-control" nameValidator banName="aaa" #name="ngModel" required> <div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger"> <div *ngIf="name.errors.required"> Name is required </div> <div *ngIf="name.errors.banName"> {{name.errors.banName.value}} was Ban! </div> </div> </div>
この場合aaa
をnameに入力した時にエラーメッセージが出てきます
name.errors
にエラー時にはデータが入ってきます
テンプレート側で{{name.errors.banName.value}}
と書いてありますが validator側でエラーの時に返す値をテンプレートで使うこともできます。
エラーメッセージとか入れたらいいかも?
まとめ
メモ程度ですが誰かのお役に立てればいいなと思います
正直公式を読んで動かしてみるのが一番理解がしやすい気がしますね
template drivenで書いたのは単に今の所しっくりきているからです、modeldrivenだとtsファイルに結構記述がよりがちなイメージだったので今の所はtemplate drivenが良さそうな気がしてます
次はもう少し複雑なvalidationを実装してみたいと思います