在 Angular 2 中异步验证表单字段(在 HTTP 请求之后)

Validate a form field asynchronously (after HTTP request) in Angular 2

想法是让用户POST表格。如果用户已经注册,则触发 API 将电子邮件字段设置为错误返回的错误。

我将响应式表单与 FormBuilder 结合使用,并尝试在订阅错误捕获器中调用验证器:

构造函数:

this.email = fb.control('', [Validators.required, Validators.pattern(/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/), SignupComponent.alreadyExist()]);
this.username = fb.control('', [Validators.required, Validators.pattern(/^([A-ZÀ-ÿa-z0-9]{2,})+(?:[ _-][A-ZÀ-ÿa-z0-9]+)*$/)]);

this.userRegisterForm = fb.group({
    email: this.email,
    username: this.username
});

自定义 alreadyExist() 验证器:

static alreadyExist(alreadyExist: boolean = false): any {

    return (control: FormControl): { [s: string]: boolean } => {

        if(alreadyExist) {
            return { emailAlreadyExist: true }
        }
    }
}

onSubmit() :

this.user.create(newUser)
    .subscribe(
        data => {
            this.router.navigate(['signin']);
        },
        error => {
            if(error.status === 401) {

                // CALL THE VALIDATOR HERE TO SET : FALSE
                SignupComponent.alreadyExist(true);
            }

            this.loading = false;
        });

似乎调用了验证器,但从未调用过其中返回的匿名方法...也许这不是一个好习惯,有人可以强调我吗?谢谢

好的,我找到了一个很好的解决方案。

在我的例子中,对于电子邮件 FormControl,我不需要自定义 validator(即使可以使用 setValidators( )).

我可以删除 alreadyExist() 并从 validators 列表中删除它的声明。

然后,我使用 FormControl 上可用的 setErrors() 方法:

onSubmit() :

this.user.create(newUser)
    .subscribe(
        data => {
            this.router.navigate(['signin']);
        },
        error => {
            if(error.status === 401) {

                // CALL THE VALIDATOR HERE TO SET : FALSE
                this.userRegisterForm.controls['email'].setErrors({
                  "emailAlreadyExist": true
                });
            }

            this.loading = false;
        });

电子邮件 FormControl 有一个新的错误,所以,通过这种方式我可以为这个错误附加一条错误消息。

哟凯文。 :) 如果我跟进,您当前的工作流程是:

  1. 让用户提交表单(以创建新帐户)。
  2. 如果创建新帐户失败,请手动将错误附加到表单。

不好。现在您在两个地方进行了验证:提交表单之前和之后。

您应该使用自定义验证器(就像您在初始代码中所做的那样)。 但是由于检查用户名或电子邮件是否被占用可能会触发 HTTP 调用,因此您必须使用 异步验证器.

在表单声明中,异步验证器出现在同步验证器之后:

this.userForm = fb.group({
  // `userExists` is in 3rd position, it's an async validator
  username: ['', Validators.required, userExists],
  password: []
});

现在是 userExists 的代码。由于它是一个异步验证器,它必须 return 一个可观察的或一个承诺:

// This is just a standard function
// but you could also put that code in a class method.
function userExists(control: AbstractControl): Observable<{[key: string]: any}> {
  // The userName to test.
  const userName = control.value;

  // NB. In a real project, replace this observable with an HTTP request
  const existingUsernames = ['kevin', 'vince', 'bernard'];
  return Observable.create(observer => {
    // If the username is taken, emit an error
    if (existingUsernames.indexOf(userName) !== -1) {
      observer.next({usernameTaken: true});
    }
    // Username is available, emit null
    else {
      observer.next(null);
    }
    // The observable MUST complete.
    observer.complete();
  });
}

使用此 Plunkr 中的代码:https://plnkr.co/edit/aPtrp9trtmwUJDJHor6G?p=preview — 尝试输入现有用户名('kevin'、'vince' 或 'bernard'),您会看到在您提交表单之前出现错误消息。

为什么您的代码不起作用?我看到几个错误:

  • 您使用了同步验证器而不是异步验证器。
  • 您的验证器函数是一个 工厂函数 (= return 另一个函数的函数)。仅当您需要使用特定参数自定义验证器时才需要它。这不是你的情况,所以你应该只使用常规函数(即在 alreadyExist() 中,只保留 return 之后的代码)。
  • 此外,提交表单后设置错误的逻辑也不理想。