AbstractControl не содержит асинхронную ошибку проверки после добавления таймера
Я создаю регистрационную форму с реактивными формами, где клиент должен вставить адрес электронной почты. Я добавил асинхронный валидатор в этот элемент управления, чтобы проверить, используется ли уже введенное электронное письмо.
this.form = this.formBuilder.group({
...,
emailGroup: this.formBuilder.group({
email: ['', [Validators.required, Validators.email], uniqueEmailValidator(this.registerService)],
confirmEmail: ['', [Validators.required]]
}, { validator: emailMatcher }),
...
});
Мой HTML выглядит так:
<div class="col-sm-6">
<div class="form-group">
<label for="reg-email">E-mailadres</label>
<input class="form-control" type="text" id="reg-email" formControlName="email" [ngClass]="{'is-invalid': form.get('emailGroup.email').errors && (form.get('emailGroup.email').touched || isSubmitted)}">
<div class="invalid-feedback">
<div *ngIf="form.get('emailGroup.email').errors?.required || form.get('emailGroup.email').errors?.email">
Please insert a valid email
</div>
<div *ngIf="form.get('emailGroup.email').errors?.uniqueEmail">
This email is already in use
</div>
</div>
</div>
</div>
Первоначально мой асинхронный валидатор выглядит так:
export function uniqueEmailValidator(registerService: RegisterService): AsyncValidatorFn {
return (c: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
return registerService.getCustomersByEmail(c.value).pipe(
map(customers => {
return customers && customers.length > 0 ? { 'uniqueEmail': true } : null;
})
);
}
}
Все работает правильно до сих пор. Когда я вставляю существующее письмо, я получаю сообщение об ошибке "это письмо уже используется".
Я заметил, что этот асинхронный валидатор выполнялся для каждого вставленного символа, и из соображений производительности я хочу избежать этого. Я решил добавить таймер на 500 мс и выполнять асинхронную проверку только тогда, когда нет активности в течение 500 мс. Это меняет мой асинхронный валидатор на это:
export function uniqueEmailValidator(registerService: RegisterService): AsyncValidatorFn {
return (c: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
return timer(500).pipe(
map(x => {
return registerService.getCustomersByEmail(c.value).pipe(
map(customers => {
return customers && customers.length > 0 ? { 'uniqueEmail': true } : null;
})
);
})
);
};
}
Валидатор выполняет работу правильно, а элемент управления электронной почтой помечается как недействительный после асинхронной проверки. Проблема в том, что мой html больше не отображает сообщение об ошибке "Это письмо уже используется". Код ошибки "uniqueEmail" не добавляется в объект ошибок моего элемента управления.
Может кто-нибудь пролить свет на этот? Заранее спасибо.
1 ответ
Мне нужно было то же самое. Так что я сделал это немного по-другому. Я создал асинхронный метод проверки, который возвращает обещание ошибки проверки в случае ошибки. Таким образом, этот метод задерживает вызов на сервер на 500 мс, и если есть какие-либо изменения во время задержки, он отменяет предыдущий тайм-аут и запускает новый. Таким образом, в конечном итоге на сервер будет отправлен только один вызов, если пользователь печатает. Посмотрите ниже.
Объявите эти две переменные
validationDelay: any;
validateUniqueEmail: any;
Определите следующий метод асинхронной проверки. Помните, я использовал экземпляр var, потому что я не мог использовать this
контекст в обещании. Так что не путайся.
validateEmail( c: FormControl): Promise<{[key: string]: any}> {
const instance = this;
return new Promise(resolve => {
if (instance.validationDelay) {
clearTimeout(instance.validationDelay);
}
instance.validationDelay = setTimeout(() => {
instance.registerService.getCustomersByEmail(c.value)
.subscribe((customers) => {
if (customers && customers.length > 0) {
resolve({
uniqueEmail: true
});
} else {
resolve(null);
}
}, (err) => {
resolve(null);
});
}, 500);
});
}
Теперь, чтобы использовать этот асинхронный метод. Инициируйте это следующим образом
ngOnInit(): void {
this.validateUniqueEmail = (control: FormControl) => {
return this.validateEmail(control);
};
}
Использовать этот validateUniqueEmail
в вашем FormControl
email: ['', [Validators.required, Validators.email], this.validateUniqueEmail]