如何在表单上实现自定义验证器,其中验证逻辑需要来自可观察对象的数据

How to implement custom validator on a form, where the validation logic requires data from an observable

我正在尝试在表单上实现自定义验证器,它将检查表单输入并将其与值数组进行比较,以确保输入值是唯一的。用于比较的数组来自 Observable。但是,当我尝试从验证方法访问它时,该数组显示为未定义,我假设是因为我以错误的顺序进行订阅,但是我无法弄清楚如何正确执行此操作。

下面是精简的代码,应该显示我正在尝试做的事情。我已经用这两个验证器函数试过了。我还尝试将表单组定义移动到 ngOnInit 中,并在填充 allUserNames 之后直接移动到实际的订阅函数中。

export class userComponent implements OnInit {

  user: user = new user;
  allUserNames: string[];

  generalForm = new FormGroup({
    userName: new FormControl(
      this.user.name, [
        Validators.required,
        Validators.maxLength(20),
        this.UniqueNameValidator
        //,this.UniqueNameValidator1(this.alluserNames)
      ])
  })

  constructor(private userService: userService) { }

  ngOnInit() {
    this.subscribeUsers();
  }

  subscribeUsers(): void {
    this.getUsers().subscribe((users) => {
      this.allUserNames = Object.keys(users).map(itm => users[itm].name);
    })
  }

  getUsers(): Observable<user[]> {
    return this.userService.getUsers();
  }

  UniqueNameValidator(allUsers: String[]): ValidatorFn {
    return (control: AbstractControl): { [key: string]: boolean } | null => {
      allUsers.forEach((userName) => {
        if (userName === control.value) {
          return { 'notUnique': true };
        }
      });
      return null;
    }
  }

  UniqueNameValidator1(control: AbstractControl): { [key: string]: boolean } | null {
    this.allUserNames.forEach((userName) => {
      if (userName === control.value) {
        return { 'notUnique': true };
      }
    });
    return null;
  }
}

我希望验证函数比较输入字符串,并且 return 如果它从 allUserNames 获得匹配则不是唯一的。但是我不断收到以下错误:

ERROR Error: Uncaught (in promise): TypeError: Cannot read property 'forEach' of undefined  

如果有人能指出正确的方向,我将不胜感激!

谢谢。

您的 this 范围有问题。如果您要控制台日志 this,您会发现它没有指向组件,而是指向您的函数。

当您进行 http 调用时,这将是异步验证器的合适位置。另外我建议在构建表单时使用 FormBuilder,所以...

generalForm: FormGroup;

constructor(private userService: userService, private fb: FormBuilder) {
  this.generalForm = this.fb.group({
    userName: [this.user.name, 
              [Validators.required, Validators.maxLength(20)], 
              [this.UniqueNameValidator.bind(this)]]
  })
}

异步验证器作为第三个参数。看到我们还使用 bind(this) 来获得 this.

的正确范围

然后验证器看起来像这样:

import { of } from 'rxjs';
import { switchMap } from 'rxjs/operators'

UniqueNameValidator(ctrl: AbstractControl) {
  return this.userService.getUsers().pipe(
      switchMap((users: user[]) => {
        // do stuff, and either return error or "of(null)"
      })
  );