Как реализовать пользовательский валидатор в форме, где логика валидации требует данных из наблюдаемой - PullRequest
0 голосов
/ 09 февраля 2019

Я пытаюсь реализовать пользовательский валидатор в форме, который проверит ввод формы и сравнит его с массивом значений, чтобы убедиться, что входное значение уникально.Массив для сравнения взят из 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;
  }
}

Я ожидаю, что функция validate сравнит входную строку и вернет не уникальный, если она получит совпадение из allUserNames.Однако я продолжаю получать следующую ошибку:

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

Я был бы очень признателен, если бы кто-то указал мне правильное направление!

Спасибо.

1 Ответ

0 голосов
/ 09 февраля 2019

У вас проблема с областью действия 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)"
      })
  );
...