リアクティブフォームを用いたカスタムバリデータの利用

リアクティブフォームでカスタムバリデータを利用する時には、フォームを表す FormGroup オブジェクトと、 それぞれのコントロールを表す FormControl オブジェクトを作成します。 そしてフォームに FormGroup オブジェクトをバインドします。

FormControl オブジェクトにはバリデータ (validators) を取り付けることができ、それぞれのコントロールで必要な検証を行うことが可能になります。

ここでは単純な入力検証を行うバリデータを作成して、バリデータの作成方法を示します。

それでは、ここではユーザー名 (Username) の長さチェックを行うカスタムバリデータを作成します。

実は長さをチェックするだけの検証なら、テンプレート駆動フォームでも input 要素に minlength 属性を設定しておくだけで実現可能です。ここの例はあくまでもバリデータの作成例としてみてください。

src/app/usernameValidator.ts に次のクラスを作成します。

import {FormControl} from '@angular/forms';

export class UsernameValidator {

  static isLongEnough(formControl: FormControl){
    return { isLongEnough: formControl.value.length > 5 };
  }

}

isLongEnough というスタティックメソッドは、値が長さ 5 より大きいときに true の値を持つオブジェクトを返します。

これを使う FormGroup と FromControl は次のように定義します。

src/app/login-form.component.ts として次を作成します。

import {Component} from '@angular/core';
import {FormGroup, FormControl, Validators} from '@angular/forms';
import {UsernameValidator} from './usernameValidator';

@Component({
  selector: 'login-form',
  templateUrl: './login-form.component.html'
})
export class LoginFormComponent {
  form1 = new FormGroup({
    username: new FormControl(
      '',
      Validators.compose([
        Validators.required,
        UsernameValidator.isLongEnough
      ])),
    password: new FormControl(
      '',
      Validators.required)
  });

  onSubmit(){
    console.log("Submitted: " + JSON.stringify(this.form1.value));
  }
}

テンプレート src/app/login-form.component.html は次の通り。

<form [formGroup]="form1" (ngSubmit)="onSubmit()" class="form-horizontal">
  <div class="form-group">
    <label class="col-sm-2 control-label">Username</label>
    <div class="col-sm-5">
      <input
        formControlName="username"
        id="username"
        name="username"
        type="text"
        class="form-control">
      <div
        *ngIf="form1.controls.username.touched
        && ( !form1.controls.username.errors.isLongEnough
          || form1.controls.username.errors.required )"
        class="alert alert-danger">
        <span class="glyphicon glyphicon-exclamation-sign"></span>
          <span *ngIf="form1.controls.username.errors.required">
            Please enter username.
          </span>
          <span *ngIf="!form1.controls.username.errors.isLongEnough">
            Username must be at least 6 character long.
          </span>
      </div>

    </div>
  </div>
  <div class="form-group">
    <label class="col-sm-2 control-label">Password</label>
    <div class="col-sm-5">
      <input
        formControlName="password"
        name="password"
        id="password"
        type="password"
        class="form-control">
      <div
        *ngIf="form1.controls.password.touched
          && !form1.controls.password.valid"
        class="alert alert-danger">
        <span class="glyphicon glyphicon-exclamation-sign"></span>
        Please enter password.
      </div>
    </div>
  </div>
  <div class="form-group">
    <div class="col-sm-offset-2 col-sm-10">
      <button
        type="submit"
        [disabled]="( !form1.controls.username.errors.isLongEnough
          || form1.controls.username.errors.required
          || !form1.controls.password.valid )"
        class="btn btn-primary">Sign in</button>
    </div>
  </div>
</form>

ポイントは form 要素で [formGroup]="form1" として、 コンポーネントのコードで作成した FormGroup 型のプロパティ form1 とバインドしているところです。

それぞれのコントロールでは、formControlName 属性で FormControl オブジェクトと接続しています。

これによって、form1.controls.username.errors とか form1.controls.password.errors などの erros オブジェクトとか、form1.controls.password.valid 属性が利用可能となり、それぞれのコントロールの状態に依存したフォームのコントロールが可能になります。

ReactiveFormsModule

リアクティブ・フォームを使うために、ReactiveFormsModule をインポートします。

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { LoginFormComponent } from './login-form.component';

@NgModule({
  declarations: [
    AppComponent,
    LoginFormComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    ReactiveFormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

以上でカスタムバリデータ付きのリアクティブ・フォームが作成できました。