目录

  • 第一节 - 验证概述

  • 第二节 - 设置基本的验证

  • 第三节 - 即将到来的验证功能

第一节 - 验证概述

同步验证器

同步验证器函数

tambourineValidator(ctrl: AbstractControl): ValidationErrors | null {
  return ctrl.value === 'tambourine' ? null :
    {tambo: {expected: 'tambourine', actual: ctrl.value }};
}

异步验证器

异步验证器函数

myAsyncValidator(ctrl: AbstractControl): Observable<ValidationErrors|null> {
  return this._http.get(`my/endpoint?username=${ctrl.value}`)
    .map(resp => resp.json().exists ? {exists: true} : null);
}

第二节 - 设置基本的验证

json-server 简介

json-server 用于基于 JSON 数据快速地创建本地模拟的 REST API。

json-server 的安装

$ npm install -g json-server

json-server 的使用

$ json-server --watch bids.json

Angular CLI 代理配置

创建 proxy.conf.json 文件

{
  "/bids": {
    "target": "http://localhost:3000",
    "secure": false
  }
}

更新 package.json 文件

{
  "scripts": {
    "start": "ng serve --proxy-config proxy.conf.json",
  }
}

创建 bids.json 文件

{
  "bids": [
    {
      "id": 1,
      "name": "Semlinker",
      "bid": "10"
    }
  ]
}

同步验证与异步验证示例

AppModule

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import {ReactiveFormsModule} from "@angular/forms";
import {HttpModule} from "@angular/http";

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpModule,
    ReactiveFormsModule
  ],
  providers: [
    {provide: 'config', useValue: {databaseURL: 'http://localhost:3000'}}
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

AppComponent

import {Component, OnInit, Inject} from '@angular/core';
import {FormGroup, FormControl, Validators,
  AbstractControl, ValidationErrors} from "@angular/forms";
import {Http} from "@angular/http";

import {Observable} from "rxjs/Observable";
import 'rxjs/add/operator/map';

interface Bid {
  id: number;
  name: string;
  bid: number
}

@Component({
  selector: 'app-root',
  template: `
    <ul *ngIf="bids">
      <li *ngFor="let bid of bids">
        {{bid.name}}: {{bid.bid}}
      </li>
    </ul>
    <div>
       <p>New Bid</p>
       <form [formGroup]="form">
          <input type="text" placeholder="Name"
            formControlName="name">
          <div class="errors" *ngIf="name.invalid && name.touched">
            Name is required.
          </div>
          <input type="number" placeholder="Bid"
            formControlName="bid">
          <div class="errors" *ngIf="bid.invalid && bid.touched">
            <div *ngIf="bid.hasError('required')">Bid is required.</div>
            <div *ngIf="bid.hasError('toolow')">
              Bid is too low, expect {{bid.errors.toolow.expected}}, current value is
              {{bid.value}}.
            </div>
          </div>
       </form>
    </div>
    <hr>
    <button (click)="post()" [disabled]="form.invalid">POST BID</button>
  `,
  styles: [`
    input.ng-invalid.ng-touched {
      border-left: 5px solid red;
    }

    input.ng-valid.ng-touched {
      border-left: 5px solid forestgreen;
    }
  `]
})
export class AppComponent implements OnInit{
  bids: Bid[];

  form = new FormGroup({
    name: new FormControl('', Validators.required),
    bid: new FormControl('', Validators.required, this.minimumBid.bind(this))
  });

  constructor(
    private _http: Http,
    @Inject('config')private config) {
  }

  ngOnInit() {
    this._http.get(`${this.config.databaseURL}/bids`)
      .map(resp => resp.json())
      .subscribe( res => this.bids = res)
  }

 // 异步验证器
  minimumBid(ctrl: AbstractControl): Observable<ValidationErrors|null> {
    return this._http.get(`${this.config.databaseURL}/bids`)
      .map(resp => resp.json())
      .map(bids => bids[bids.length - 1])
      .map(bid => {
        return ctrl.value > bid.bid ? null : {toolow: {expected: bid.bid}}
      });
  }

  get name() { return this.form.get('name'); }

  get bid() { return this.form.get('bid'); }

 // 新增Bid
  post() {
    let newBid = {...this.form.value, id: ++this.bids[this.bids.length - 1].id};
    this._http.post(`${this.config.databaseURL}/bids`, newBid)
      .map(resp => resp.json())
      .subscribe(resp => {
        this.bids.push(resp);
      });
    this.form.reset();
  }
}

第三节 - 即将到来的验证功能

验证流程

流程一

流程二

验证管线

基于上面的内容我们能做什么

  • 定制验证器链

  • 控制验证顺序和验证时间

  • 当应用程序出现错误时,发出错误信息

  • 基于 Push 验证

定制验证器链示例

// ValidatorChain 目前还不支持
export class AppComponent implements OnInit{
  bids: Bid[];

  form = new FormGroup({
    name: new FormControl('', Validators.required),
    bid: new FormControl('', {chain: this.myChain.bind(this)})
  });

  myChain(ctrl: AbstractControl): ValidatorChain {
      return (obs) => {
        return obs.map(Validators.required)
            .switchMap(errs => errs ? Observable.of(errs) :
            this.minimumBid(ctrl)).startWith(PENDING)
  }
}
03-05 19:17