【问题标题】:Angular Reactive forms don't wait for custom async validatorsAngular Reactive 表单不等待自定义异步验证器
【发布时间】:2020-01-02 12:23:47
【问题描述】:

我的 angular-CLI 应用程序中有一个登录组件。我有字段电子邮件和密码。我创建了两个自定义验证 => 一个用于检查用户是否存在,另一个用于检查密码是否与用户匹配。我检查了内置验证器的工作情况,例如必填字段和有效电子邮件。他们工作正常。问题是我的自定义验证器仅在调用提交后才显示错误。 响应式表单不等待自定义异步验证器解决。

这是我的代码:

import {Component, OnInit} from '@angular/core';
import {FormGroup, FormBuilder, Validators} from '@angular/forms';
import {AuthService} from '../auth.service';
import {noUser, pwdMisMatch} from '../validators';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {

  form: FormGroup;
  submitted = false;
  returnUrl: string;

  constructor(private formBuilder: FormBuilder, public authService: AuthService) {
    this.form = this.formBuilder.group({
        email: ['', [Validators.required, Validators.email]],
        password: ['', Validators.required],
      },
      {
        validators: [noUser(authService, 'email'), pwdMisMatch(authService, 'email', 'password')]
        , updateOn: 'blur'
      }
    );
  }

  ngOnInit() {
    this.returnUrl = '/dashboard';
    this.authService.logout();
  }

  get f() {
    return this.form.controls;
  }

  onSubmit() {
    this.submitted = true;

    // stop here if form is invalid
    if (this.form.invalid) {
      return;
    } else {
      alert('Success');
    }
  }

}

这是我的自定义验证器文件:

import {FormGroup} from '@angular/forms';
import {AuthResponse, AuthService} from './auth.service';

export function MustMatch(controlName: string, matchingControlName: string) {
  return (formGroup: FormGroup) => {
    const control = formGroup.controls[controlName];
    const matchingControl = formGroup.controls[matchingControlName];

    if (matchingControl.errors && !matchingControl.errors.mustMatch) {
      // return if another validator has already found an error on the matchingControl
      return;
    }

    // set error on matchingControl if validation fails
    if (control.value !== matchingControl.value) {
      matchingControl.setErrors({ mustMatch: true });
    } else {
      matchingControl.setErrors(null);
    }
  };
}

export function userExist(authservice: AuthService, controlName: string) {
  return (formGroup: FormGroup) => {
    const control = formGroup.controls[controlName];

    if (control.errors && !control.errors.CheckUser) {
      // return if another validator has already found an error on the matchingControl
      return;
    }

    // set error on matchingControl if validation fails
    authservice.checkUser(control.value).subscribe((res: AuthResponse) => {
      if (res.ok) {
        control.setErrors({ userExist: true });
      } else {
        control.setErrors(null);
      }
    });
  };
}

export function noUser(authService: AuthService, controlName: string) {
  return (formGroup: FormGroup) => {
    const control = formGroup.controls[controlName];

    if (control.errors && !control.errors.noUser) {
      // return if another validator has already found an error on the matchingControl
      return;
    }

    // set error on matchingControl if validation fails
    authService.checkUser(control.value).subscribe((res: AuthResponse) => {
      if (!res.ok) {
        control.setErrors({ noUser: true });
      } else {
        control.setErrors(null);
      }
    });
  };
}

export function pwdMisMatch(authService: AuthService, controlName: string, secureControlName: string) {
  return (formGroup: FormGroup) => {
    const control = formGroup.controls[controlName];
    const secureControl = formGroup.controls[secureControlName];

    if (control.errors || secureControl.errors && !secureControl.errors.pwdMisMatch) {
      // return if another validator has already found an error on the matchingControl
      return;
    }

    // set error on matchingControl if validation fails
    authService.verifyPassword(control.value, secureControl.value).subscribe((res: AuthResponse) => {
      if (!res.ok) {
        secureControl.setErrors({ pwdMisMatch: true });
      } else {
        control.setErrors(null);
      }
    });
  };
}

我试过这个answer,但问题没有解决。请帮忙。

更新:my angular repo

【问题讨论】:

  • 您的意思是在提交表单之前,on submit 会被调用?
  • no no,"this.form.invalid" 的值失败,并在自定义表单验证解决之前显示成功消息。
  • 我需要使表单无效,直到自定义验证完成。我认为手动设置值不是一个好方法。
  • 有什么方法可以让响应式表单等待自定义验证解决。只有在表单验证完成时才执行提交功能
  • 你试过FormGroup的待定属性吗?

标签: angular validation authentication angular-cli angular-reactive-forms


【解决方案1】:

Angular customValidator 函数应该返回错误或 null 才能工作。

FormGroup 具有待处理状态,您可以使用它来检查异步验证器是否已完成。

试试这个:

export function noUser(authService: AuthService, controlName: string) {
  return (formGroup: FormGroup) => {
    const control = formGroup.controls[controlName];

    if (control.errors && !control.errors.noUser) {
      // return if another validator has already found an error on the matchingControl
      return;
    }

    // set error on matchingControl if validation fails
    authService.checkUser(control.value).subscribe((res: AuthResponse) => {
      if (!res.ok) {
        return { noUser: true };
      } else {
        return null;
      }
    });
  };
}

export function pwdMisMatch(authService: AuthService, controlName: string, secureControlName: string) {
  return (formGroup: FormGroup) => {
    const control = formGroup.controls[controlName];
    const secureControl = formGroup.controls[secureControlName];

    if (control.errors || secureControl.errors && !secureControl.errors.pwdMisMatch) {
      // return if another validator has already found an error on the matchingControl
      return;
    }

    // set error on matchingControl if validation fails
    authService.verifyPassword(control.value, secureControl.value).subscribe((res: AuthResponse) => {
      if (!res.ok) {
        rerurn { pwdMisMatch: true };
      } else {
        return null;
      }
    });
  };
}

 onSubmit() {
        this.submitted = true;

        // stop here if form is invalid
        if (this.form.pending && this.form.invalid) {
          return;
        } else {
          alert('Success');
        }
      }

【讨论】:

  • 这没有做任何改变。表单的行为与以前一样
  • 我试过你的答案,但错误一直在发生。我发现在触发表单验证之前显示成功消息!
  • 我为你添加了我的 Angular 仓库
  • 我没有收到任何错误,但是尽管验证失败,反应式表单仍会执行提交代码。
  • 你的意思是invalid false?
【解决方案2】:

也许您应该像这样将其标识为异步验证器:

constructor(private formBuilder: FormBuilder, public authService: AuthService) {
    this.form = this.formBuilder.group(
        {
            email: ['', [Validators.required, Validators.email]],
            password: ['', Validators.required],
        },
        {
            asyncValidators: [noUser(authService, 'email'), pwdMisMatch(authService, 'email', 'password')]
            , updateOn: 'blur'
        }
    );
}

【讨论】:

  • 我尝试了您的回答,我提供了一封有效的电子邮件,其中显示错误“未找到用户”。但表单提交代码也在运行。
  • 表单没有等待验证完成
  • 我为你添加了我的 Angular 仓库
【解决方案3】:

您的异步验证函数不返回 Promise 或 Observable 的问题

来自角度文档:

异步验证器:接受一个控件实例并返回一个 Promise 或 Observable 的函数,该 Promise 或 Observable 稍后会发出一组验证错误或 null。您可以在实例化 FormControl 时将它们作为第三个参数传入

不要在此处使用订阅:

authservice.checkUser(control.value).subscribe()

改用 pipe() 转换:

authservice.checkUser(control.value). pipe(map(res => res && res.ok ? ({ userExist: true }) : null )) 

【讨论】:

  • 我试过你的答案。它没有用。此外,它无法添加诸如“ userExist: true ”之类的错误。该错误甚至没有显示在表单中。
  • “不显示”是什么意思?您是否尝试在控制台中使用 ng.probe($0).componentInstance 进行调试?
  • checkUser(email: any) { const url = 'localhost:3000/api/auth/user';常量标头: HttpHeaders = new HttpHeaders({ 'Content-Type': 'application/json' });常量正文:任何 = JSON.parse(JSON.stringify({ email }));常量选项 = { 标题 }; return this.http.post(url, body, options) .pipe( map(data => new AuthResponse(data)) ); }
  • 我应该再管一次吗?
  • 没关系。你确定你在 Json.parse 和 stringify 上没有异常吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-06
  • 2023-03-12
  • 1970-01-01
  • 2017-11-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多