参考资料
https://angular.cn/guide/form-validation#async-validation
知识点:
1、异步验证器接口:AsyncValidatorFn和AsyncValidators
2、必须返回承诺(Promise)或可观察对象(Observable)
3、返回的可观察对象必须是有限的,比如向服务器发送请求
注意:
异步验证在同步验证之后执行,并且只有当同步验证成功了之后才会执行,比如用户名格式如果不对会先校验格式,等格式校验成功之后再校验唯一性。
异步验证的使用场景:
需要向后端发请求判断是否验证通过,比如注册时验证用户名是否被占用
建立表单模型
this.targetForm = new FormGroup({
'userName': new FormControl('', {
// asyncValidators中加入异步验证器
asyncValidators: [this.uniqueUserNameValidator.validateUserName.bind(this.uniqueUserNameValidator)],
})
})
验证器实现
@Injectable({ providedIn: 'root' })
export class UniqueUserNameValidator implements AsyncValidator {
constructor(private service: ValidationService) {}
validateUserName(ctrl: AbstractControl):
Promise<ValidationErrors | null>| Observable<ValidationErrors | null> {
return this.service.checkUserName(ctrl.value).pipe(
// 如果已存在,return出错误信息
map(isExist=> (isExist ? { uniqueUserName: true } : null)),
catchError(() => null)
);
}
}
@Injectable({ providedIn: 'root' })
export class ValidationService {
checkUserName(userName: string): Observable<boolean> {
// 发送http请求,将结果return
const path = `***`
this.httpService.get(path, {userName: userName})
.subscribe(res => {
// of会创建一个 Observable,它会依次发出由你提供的参数,最后发出完成通知。
return of(res.result)
})
}
}
页面提示
<div class="form-group">
<label for="userName">用户名</label>
<input id="userName" class="form-control" formControlName="userName">
<app-spinner *ngIf="userName.pending"></app-spinner> // 发送请求过程中加个转圈提醒
<div *ngIf="userName.invalid" class="alert alert-danger">
<div *ngIf="userName.errors?.uniqueUserName"> // 占用提醒
该用户名已被占用,请修改!
</div>
</div>
</div>
性能优化
如果每次按键都触发请求会给后端 API 带来沉重的负担,应该避免,可以在失焦时触发请求,优化如下:
new FormControl('', {updateOn: 'blur'});