目录
- 第一节 - 验证概述
- 第二节 - 设置基本的验证
- 第三节 - 即将到来的验证功能
第一节 - 验证概述
同步验证器
同步验证器函数
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)
}
}