在实际工作中,我们经常需要一个基于后端API验证值的验证器。为此,Angular提供了一种定义自定义异步验证器的简便方法。
本文将介绍如何为Angular应用程序创建自定义异步验证器。
通常你会调用一个真正的后端,但是在这里我们将创建一个虚拟的JSON文件,我们可以通过使用Http服务来调用它。如果正在使用Angular CLI,则可以将JSON文件放在/assets文件夹中,它将自动可用;
/assets/users.json
[
{ "name": "Paul", "email": "paul@example.com" },
{ "name": "Ringo", "email": "ringo@example.com" },
{ "name": "John", "email": "john@example.com" },
{ "name": "George", "email": "george@example.com" }
]
注册服务
接下来,让我们创建一个具有checkEmailNotTaken
方法的服务,该方法触发对我们的JSON文件的http GET调用。这里我们使用RxJS
的延迟运算符来模拟一些延迟:
signup.service.ts
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/delay';
@Injectable()
export class SignupService {
constructor(private http: Http) {}
checkEmailNotTaken(email: string) {
return this.http
.get('assets/users.json')
.delay(1000)
.map(res => res.json())
.map(users => users.filter(user => user.email === email))
.map(users => !users.length);
}
}
请注意我们如何筛选与提供给方法的用户具有相同电子邮件的用户。然后我们再次映射结果并进行测试以确保我们得到一个空置对象。
在真实场景中,您可能还想使用debounceTime和distinctUntilChanged运算符的组合,如我们在创建实时搜索的帖子中所讨论的。引入一些这样的去抖动将有助于将发送到后端API的请求数量保持在最低水平。
组件和异步验证器
我们的简单组件初始化我们的反应形式并定义我们的异步验证器:validateEmailNotTaken。请注意我们的FormBuilder.group
声明中的表单控件如何将异步验证器作为第三个参数。这里我们只使用一个异步验证器,但是你想在数组中包含多个异步验证器:
app.component.ts
import { Component, OnInit } from '@angular/core';
import {
FormBuilder,
FormGroup,
Validators,
AbstractControl
} from '@angular/forms';
import { SignupService } from './signup.service';
@Component({ ... })
export class AppComponent implements OnInit {
myForm: FormGroup;
constructor(
private fb: FormBuilder,
private signupService: SignupService
) {}
ngOnInit() {
this.myForm = this.fb.group({
name: ['', Validators.required],
email: [
'',
[Validators.required, Validators.email],
this.validateEmailNotTaken.bind(this)
]
});
}
validateEmailNotTaken(control: AbstractControl) {
return this.signupService.checkEmailNotTaken(control.value).map(res => {
return res ? null : { emailTaken: true };
});
}
}
我们的验证器与典型的自定义验证器非常相似。这里我们直接在组件类中定义了验证器而不是单独的文件。这样可以更轻松地访问我们注入的服务实例。另请注意我们如何绑定此值以确保它指向组件类。
我们还可以在自己的文件中定义我们的异步验证器,以便更容易地重用和分离关注点。唯一棘手的部分是找到一种方法来提供我们的服务实例。在这里,例如,我们创建一个具有createValidator静态方法的类,该方法接收我们的服务实例并返回我们的验证器函数:
/validators/async-email.validator.ts
import { AbstractControl } from '@angular/forms';
import { SignupService } from '../signup.service';
export class ValidateEmailNotTaken {
static createValidator(signupService: SignupService) {
return (control: AbstractControl) => {
return signupService.checkEmailNotTaken(control.value).map(res => {
return res ? null : { emailTaken: true };
});
};
}
}
然后,回到我们的组件中,我们导入ValidateEmailNotTaken类,我们可以使用这样的验证器:
ngOnInit() {
this.myForm = this.fb.group({
name: ['', Validators.required],
email: [
'',
[Validators.required, Validators.email],
ValidateEmailNotTaken.createValidator(this.signupService)
]
});
}
模板
在模板中,事情真的很简单:
app.component.html
<form [formGroup]="myForm">
<input type="text" formControlName="name">
<input type="email" formControlName="email">
<div *ngIf="myForm.get('email').status === 'PENDING'">
Checking...
</div>
<div *ngIf="myForm.get('email').status === 'VALID'">
😺 Email is available!
</div>
<div *ngIf="myForm.get('email').errors && myForm.get('email').errors.emailTaken">
😢 Oh noes, this email is already taken!
</div>
</form>
您可以看到我们根据电子邮件表单控件上status属性的值显示不同的消息。对于可能的值状态是VALID,INVALID,PENDING和禁用。如果异步验证错误输出我们的emailTaken错误,我们也会显示错误消息。
使用异步验证器验证的表单字段在验证待处理时也将具有ng-pending类。这样可以轻松设置当前待验证字段的样式。
✨你有它!使用后端API检查有效性的简便方法。