Как вызвать асинхронный валидатор c после неприкосновения поля ввода в Angular с реактивными формами? - PullRequest
0 голосов
/ 31 января 2020

Я пытаюсь добиться того, чтобы мой asyn c валидатор срабатывал только после того, как я не трогал поле ввода.

Прямо сейчас я запускаю асинхронный c валидатор с каждой буквой, которую я пишу на вход поле. Так как валидатор asyn c запускает облачную функцию Firebase, которая даже запускает чтение в Firebase Firestore, это стоит слишком дорого только для проверки, существует ли указанное c имя пользователя.

Так есть ли способ вызвать валидатор asyn c только после того, как пользователь коснется другого поля ввода или кнопки? Или даже что-то совершенно другое, что может решить эту проблему?

Вот мой код:

SignUpComponent TS

export class SignUpComponent implements OnInit {
  signUpForm: FormGroup;

  constructor(
    private authService: AuthService,
    private userService: UserService,
    private formBuilder: FormBuilder,
    private router: Router) {
  }

  ngOnInit() {
    this.signUpForm = this.formBuilder.group({
      username: [null, Validators.required, this.existingUsername.bind(this)],
      ...
    });
  }

  signUp() {
    ...
  }

  // performance is bad - one doc read and one function call for each letter entered
  existingUsername(control: FormControl): Promise<any> | Observable<any> {
    return this.userService.checkUsername(control.value)
      .pipe(
        map(res => {
          return res.usernameAvailable ? null : {existingUsername: true};
        })
      );
  }
}

SignUpComponent HTML

<form [formGroup]="signUpForm" (ngSubmit)="signUp()">
  <mat-form-field>
    <input matInput placeholder="Username" formControlName="username">
    <mat-error *ngIf="signUpForm.get('username').hasError('required')">
      Please insert a username!
    </mat-error>
    <mat-error *ngIf="signUpForm.get('username').hasError('existingUsername')">
      Username is already in use!
    </mat-error>
    <mat-hint *ngIf="signUpForm.status === 'PENDING'">
      Checking...
    </mat-hint>
  </mat-form-field>
</form>

Ответы [ 2 ]

1 голос
/ 31 января 2020

Я думаю, что вы хотите обновить форму на blur

const control = new FormControl('', { updateOn: 'blur' });
1 голос
/ 31 января 2020

используйте параметр updateOn в вашем элементе управления формой, что-то вроде:

username: [null, { 
  validators: Validators.required, 
  asyncValidators: this.existingUsername.bind(this), 
  updateOn: 'blur'}],

, что говорит angular запускать проверку только при размытии, а не при изменении значения.

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

 return timer(300).pipe(switchMap(() => /* actual validator */ ))

Это обеспечит выполнение запроса проверки только в том случае, если пользователь не изменил значение в течение 300 мс, поскольку предыдущая проверка будет отменена, если пользователь изменит значение.

...