【发布时间】:2020-05-01 19:48:51
【问题描述】:
我正在使用 Angular 8 和 Bootstrap 做一个 Web 应用程序。我有一个只有一个输入的表单来输入一个 5 位数字(作为字符串)。我想验证这个数字在数据库中是唯一的。
我试图实现一个异步验证器。它检查数字是否唯一,如果不是,则应返回错误以在模板中显示消息。
brand: Brand;
gambleForm: FormGroup;
constructor(
private brandService: BrandService,
private fb: FormBuilder,
) {
this.gambleForm = fb.group({
number: ['', [ // Default validators
Validators.required,
Validators.minLength(5)
]]
});
}
ngOnInit() {
// Get the id of the entity coming from the router
const id = this.route.snapshot.paramMap.get('id');
this.getBrandData(id);
}
getBrandData(id: string) {
this.brandService.getBrandById(id).subscribe(res => {
this.brand = res;
this.setNumberAsyncValidator();
});
}
// Set async validator
setNumberAsyncValidator() {
this.gambleForm.get('number').setAsyncValidators([
CustomValidator.checkNumberDisponibility(
this.brandService,
this.brand.id,
'jsjdlnsdf3jn234'
)
]);
}
CustomValidators 类:
import {AbstractControl, AsyncValidatorFn} from '@angular/forms';
import {catchError, debounceTime, map, switchMap} from 'rxjs/operators';
import {Observable, of} from 'rxjs';
import {BrandService} from '../core/services/brand.service';
function isEmptyInputValue(value: any): boolean {
return value === null || value.length === 0;
}
export class CustomValidator {
static checkNumberDisponibility(brandService: BrandService, brandId: string, raffleId: string): AsyncValidatorFn {
return (control: AbstractControl):
Promise<{ [key: string]: any } | null>
| Observable<{ [key: string]: any } | null> => {
if (isEmptyInputValue(control.value)) {
return of(null);
} else if (control.value.length !== 5) {
return of(null);
} else {
return control.valueChanges.pipe(
debounceTime(500),
switchMap(_ =>
// This method returns: Observable<any[]>
brandService.getGamblesWithTheNumber(brandId, raffleId, control.value)
.pipe(
map(gambles => {
// The "exhausted" error is not present in the FormControl
return gambles.length ? {exhausted: true} : null;
}),
catchError(err => {
console.log('Number validator error: ' + err);
return of(null);
})
)
)
);
}
};
}
}
FormControl 中不存在我返回的错误,因此模板中未显示我的自定义消息。换句话说,FormControl 在其“错误”对象中没有任何内容。
我试过没有结果:
setNumberAsyncValidator() {
this.gambleForm.get('number').setAsyncValidators([
CustomValidator.checkNumberDisponibility(
...
)
]);
this.gambleForm.get('number').updateValueAndValidity(); // Notice this
}
但是,这样做确实有效:
map(gambles => {
return gambles.length ? control.setErrors({exhausted: true}) : null;
}),
另外,这样做也可以:
return control.valueChanges.pipe(
debounceTime(500),
switchMap(_ =>
brandService.getGamblesWithTheNumber(brandId, raffleId, control.value)
.pipe(
map(gambles => {
console.log('Gambles count: ' + gambles.length);
return gambles.length ? {exhausted: true} : null;
}),
catchError(err => {
console.log('Number validator error: ' + err);
return of(null);
})
)
),
first(), // Notice this
);
也许返回的 Observable 没有正确完成,这就是我需要使用 first () 的原因?我在这里很困惑。
我发现的所有异步验证器示例都使用第一种方法,返回{exhausted: true}。出于某种原因,它对我不起作用。我的第二种方法,使用control.setErrors()(我正在测试并进行反复试验并且它有效),我认为这不是最好的方法。甚至 Angular 的文档也没有这样做。
为什么在 FormControl 的错误中没有返回 {exhausted: true}?我错过了什么?
我的目标是正确返回 {exhausted: true} 并将其包含在 FormControl 错误中以在模板中显示我的自定义消息。
【问题讨论】:
-
你需要在 setNumberAsyncValidator() 函数中返回错误。并添加此函数来验证gambleForm中的数字表单控件。
-
你必须在你的异步验证器中使用冷可观察。
标签: angular angular-reactive-forms