notebook

都内でWEB系エンジニアやってます。

angularでvalidation(template driven)

今回は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された
  • 参照

Angular - Form Validation

https://angular.io/guide/form-validationangular.io

  • app.component.scss
.ng-invalid:not(form) {
  border-left: 3px solid #CC3333;
  border-radius: 3px;
}

f:id:swfz:20170915031053p:plain

公式にも乗っていますがこんな感じで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を実装してみたいと思います