反跳异步验证器
Debounce async Validator
我有一个工作的异步验证器,它向服务器发出 HTTP 请求以检查用户名是否已被使用。
因为我不想在每次击键后调用 API,所以我需要对输入流进行去抖动。
我第一次在服务中有 throttleTime
,但是 SO 上的另一个主题说这必须在订阅者身上,但还没有运气!
我的组件:
this.form = this._fb.group(
{
username: ['', [Validators.required, Validators.maxLength(50), NoWhitespaceValidator], [IsUserIdFreeValidator.createValidator(this._managementService)]]
});
我的验证者:
export class IsUserIdFreeValidator {
static createValidator(_managementService: ManagementService) {
return (control: AbstractControl) => {
return _managementService.isUserIdFree(control.value)
.pipe(
throttleTime(5000),
(map(
(result: boolean) => result === false ? { isUserIdFree: true } : null))
);
};
}
}
我的服务:
public isUserIdFree(userId: string): Observable<{} | boolean | HttpError> {
const updateUserCheck: UpdateUserCheck = new UpdateUserCheck();
updateUserCheck.userID = userId;
return this._httpClient.post<boolean>('UserManagementUser/IsUserIdFree', updateUserCheck));
}
这应该可以解决问题:
static createValidator(_managementService: ManagementService) {
const subject = new BehaviorSubject('');
const observable = subject.asObservable().pipe(
debounceTime(1000),
switchMap(val => _managementService.isUserIdFree(val)),
map((isUserIdFree: boolean) => isUserIdFree ? null : { userIdTaken : true }),
);
return (control: AbstractControl) => {
subject.next(control.value);
return observable.pipe(takeUntil(timer(5000))); // acts as a way to make observable finite
}
}
去抖动应该发生在控件发出的值上,而不是从 http 服务返回的结果上。我们首先在可观察流上发出值并将其通过 distinctUntilChanged
进行管道传输,这确保只有与上次发出的值相比不同的值才能通过管道的那个阶段。 debounceTime(x)
确保仅在 'x' 毫秒数后发出最后一个值。
switchMap
运算符获取控制值并向后端发出获取请求,并将新的可观察对象传递到管道的下一阶段。最后,我已将您现有的地图运算符应用于后端的结果,以生成相应的错误消息。
我可以通过另一种方式解决这个问题,即通过控件上名为“updateOn”的 属性。
使用模板驱动的表单:
<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">
反应形式:
new FormControl('', {updateOn: 'blur'});
如果您使用表单生成器,因为 Angular 7 您可以使用 this:
this.form = this._fb.group({
email: ['', [CustomValidators.email], null, 'blur'],
});
使用 observables,我做到了:
form = this._fb.group({
email: [null, [Validators.required, Validators.email], [this.validateEmailNotTaken.bind(this)]]
})
private _emailCancel$ = new Subject<void>();
validateEmailNotTaken(control: AbstractControl): Observable<object> {
this._emailCancel$.next();
return race(
timer(3000).pipe(map(_ => true)),
this._emailCancel$.pipe(
map(_ => false)
)
).pipe(
take(1),
filter(val => !!val),
switchMap(() => this.isEmailTaken(control.value).pipe(
map(res => res ? {isTaken: true } : null)
))
);
}
对于找到此问题和答案集的任何人,恕我直言,此问题与 . I think this 重复,是正确的,除非您喜欢 Promises。我打算 post 那里有一个较新的答案 (Angular 9+) 解决方案。
编辑: 如何做到这一点。
顺便说一句,在遇到这个问题和另一个问题之前,我尝试了这里列出的所有三个答案。你可以让它们全部工作,但我链接到的答案无疑是最优雅的。很难弄清楚,因为它依赖于隐藏在 Angular 框架中的行为,而这些行为确实应该包含在文档中。
我有一个工作的异步验证器,它向服务器发出 HTTP 请求以检查用户名是否已被使用。 因为我不想在每次击键后调用 API,所以我需要对输入流进行去抖动。
我第一次在服务中有 throttleTime
,但是 SO 上的另一个主题说这必须在订阅者身上,但还没有运气!
我的组件:
this.form = this._fb.group(
{
username: ['', [Validators.required, Validators.maxLength(50), NoWhitespaceValidator], [IsUserIdFreeValidator.createValidator(this._managementService)]]
});
我的验证者:
export class IsUserIdFreeValidator {
static createValidator(_managementService: ManagementService) {
return (control: AbstractControl) => {
return _managementService.isUserIdFree(control.value)
.pipe(
throttleTime(5000),
(map(
(result: boolean) => result === false ? { isUserIdFree: true } : null))
);
};
}
}
我的服务:
public isUserIdFree(userId: string): Observable<{} | boolean | HttpError> {
const updateUserCheck: UpdateUserCheck = new UpdateUserCheck();
updateUserCheck.userID = userId;
return this._httpClient.post<boolean>('UserManagementUser/IsUserIdFree', updateUserCheck));
}
这应该可以解决问题:
static createValidator(_managementService: ManagementService) {
const subject = new BehaviorSubject('');
const observable = subject.asObservable().pipe(
debounceTime(1000),
switchMap(val => _managementService.isUserIdFree(val)),
map((isUserIdFree: boolean) => isUserIdFree ? null : { userIdTaken : true }),
);
return (control: AbstractControl) => {
subject.next(control.value);
return observable.pipe(takeUntil(timer(5000))); // acts as a way to make observable finite
}
}
去抖动应该发生在控件发出的值上,而不是从 http 服务返回的结果上。我们首先在可观察流上发出值并将其通过 distinctUntilChanged
进行管道传输,这确保只有与上次发出的值相比不同的值才能通过管道的那个阶段。 debounceTime(x)
确保仅在 'x' 毫秒数后发出最后一个值。
switchMap
运算符获取控制值并向后端发出获取请求,并将新的可观察对象传递到管道的下一阶段。最后,我已将您现有的地图运算符应用于后端的结果,以生成相应的错误消息。
我可以通过另一种方式解决这个问题,即通过控件上名为“updateOn”的 属性。
使用模板驱动的表单:
<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">
反应形式:
new FormControl('', {updateOn: 'blur'});
如果您使用表单生成器,因为 Angular 7 您可以使用 this:
this.form = this._fb.group({
email: ['', [CustomValidators.email], null, 'blur'],
});
使用 observables,我做到了:
form = this._fb.group({
email: [null, [Validators.required, Validators.email], [this.validateEmailNotTaken.bind(this)]]
})
private _emailCancel$ = new Subject<void>();
validateEmailNotTaken(control: AbstractControl): Observable<object> {
this._emailCancel$.next();
return race(
timer(3000).pipe(map(_ => true)),
this._emailCancel$.pipe(
map(_ => false)
)
).pipe(
take(1),
filter(val => !!val),
switchMap(() => this.isEmailTaken(control.value).pipe(
map(res => res ? {isTaken: true } : null)
))
);
}
对于找到此问题和答案集的任何人,恕我直言,此问题与
编辑:
顺便说一句,在遇到这个问题和另一个问题之前,我尝试了这里列出的所有三个答案。你可以让它们全部工作,但我链接到的答案无疑是最优雅的。很难弄清楚,因为它依赖于隐藏在 Angular 框架中的行为,而这些行为确实应该包含在文档中。