【问题标题】:Angular Custom Async Validation using Reactive Form and await in service method使用响应式表单和等待服​​务方法的角度自定义异步验证
【发布时间】:2020-12-31 00:18:09
【问题描述】:

我正在尝试将 Angular 中的 AsyncValidation 与具有 async/await 的服务方法一起使用,以测试用户名是否存在。我不知道如何转换服务中的返回签名(user.service.ts)

Promise<boolean> 

e.g. 

async isUserNameAvailable(userName: string): Promise<boolean> {
}

Promise<ValidationErrors | null> | Observable<ValidationErrors | null>

e.g.

validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
}

在验证器/指令中。

user.service.ts:

async isUserNameAvailable(userName: string): Promise<boolean> {
    var query = this.db.collection("users").where("name", "==", userName);

    try {
      const documentSnapshot = await query.get();

      if (documentSnapshot.empty) {
        return true;
      } else {
        return false;
      }
    } catch (error) {
      console.log('Error getting documents', error);
    }
  }

existing-username-validator.directive.ts

import { Directive } from '@angular/core';
import { UserService } from './user.service';
import { AbstractControl, ValidationErrors, NG_ASYNC_VALIDATORS, AsyncValidatorFn, AsyncValidator } from '@angular/forms';
import { Observable, timer } from 'rxjs';
import { map, filter, switchMap } from 'rxjs/operators';

export function existingUsernameValidator(userService: UserService): AsyncValidatorFn {
  return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {

    let debounceTime = 500; //milliseconds
    return Observable.timer(debounceTime).switchMap(()=> { //ERROR BECAUSE OF TIMER
      return userService.isUserNameAvailable(control.value).map( //ERROR BECAUSE OF map
        users => {
          return (users && users.length > 0) ? {"usernameExists": true} : null;
        }
      );
    });
  };
} 

@Directive({
  selector: '[appExistingUsernameValidator]',
  providers: [{provide: NG_ASYNC_VALIDATORS, useExisting: ExistingUsernameValidatorDirective, multi: true}]
})
export class ExistingUsernameValidatorDirective implements AsyncValidator {

  constructor(private userService: UserService) {  }

  validate(control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
    return existingUsernameValidator(this.userService)(control);  
  }
}

user.component.ts:

name: new FormControl('', {
      validators: [Validators.required],
      asyncValidators: [existingUsernameValidator(this.userService)]
    }),

Stackblitz:https://stackblitz.com/edit/angular-ivy-cd866c?file=src%2Fapp%2Fuser.service.ts

有谁知道如何使用响应式表单来实现这一点?

【问题讨论】:

  • 让我检查一下
  • 非常感谢。我刚刚添加了一个 Stackblitz。

标签: angular firebase validation async-await angular-reactive-forms


【解决方案1】:

刚刚纠正了 Stackblitz 中的 existingUsernameValidator

export function existingUsernameValidator(userService: UserService): AsyncValidatorFn {
  return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
    let debounceTime = 500; //milliseconds
    const debounceTimer = timer(debounceTime)
    return debounceTimer.pipe(switchMap(()=> {
      return userService.isUserNameAvailable(control.value)
      .then(result => {
          return result ? {"usernameExists": true} : null;
      });
    }));
  };
} 

UserService 中更新了isUserNameAvailable

  async isUserNameAvailable(userName: string): Promise<boolean> {
    const query = this.db.collection("users").where("name", "==", userName);

    return query.get()
    .then(function(documentSnapshot) {
      return (documentSnapshot.empty as boolean) 
    })
    .catch(function(error) {
      console.log("Error getting documents: ", error);
      return false;
    });
  }

试试看,如果现在一切就绪,请告诉我。也在 stackblitz 上更新

希望对你有帮助!

编辑:清理后的代码

代办功能

export function existingUsernameValidator(userService: UserService): AsyncValidatorFn {
  return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
    const debounceTime = 500; //milliseconds
    return timer(debounceTime).pipe(switchMap(()=> {
      return userService.isUserNameAvailable(control.value)
      .then(result => result ? {"usernameExists": true} : null);
    }));
  };
} 

用户服务

async isUserNameAvailable(userName: string): Promise<boolean> {
  return this.db.collection("users").where("name", "==", userName).get()
  .then(documentSnapshot => documentSnapshot.empty as boolean)
  .catch(error => {
    console.log("Error getting documents: ", error);
    return false;
  });
}

【讨论】:

  • 再次感谢。又近了一步。您的解决方案消除了 timer() 和 map() 引发的错误。但是,然后会导致以下错误:“类型 'boolean' 上不存在属性 'then'”。
  • 好的。检查它
【解决方案2】:

existingUsernameValidator 更新如下,

import {UserService} from './user.service';
import {AbstractControl, ValidationErrors, NG_ASYNC_VALIDATORS, AsyncValidatorFn, AsyncValidator} from '@angular/forms';
import {from, Observable, timer} from 'rxjs';
import {map, debounceTime} from 'rxjs/operators';

export function existingUsernameValidator(userService: UserService): AsyncValidatorFn {
  return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {

    return from(userService.isUserNameAvailable(control.value)).pipe(debounceTime(500),
      map(
        users => {
          return users ? {'usernameExists': true} : null;
        }
      )
    );
  };
}

希望对你有帮助

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-29
    • 2018-07-08
    • 1970-01-01
    • 2018-09-06
    • 2019-11-12
    • 2019-10-10
    相关资源
    最近更新 更多