在 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 有一个新的错误,所以,通过这种方式我可以为这个错误附加一条错误消息。
哟凯文。 :) 如果我跟进,您当前的工作流程是:
- 让用户提交表单(以创建新帐户)。
- 如果创建新帐户失败,请手动将错误附加到表单。
不好。现在您在两个地方进行了验证:提交表单之前和之后。
您应该使用自定义验证器(就像您在初始代码中所做的那样)。
但是由于检查用户名或电子邮件是否被占用可能会触发 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
之后的代码)。
- 此外,提交表单后设置错误的逻辑也不理想。
想法是让用户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 有一个新的错误,所以,通过这种方式我可以为这个错误附加一条错误消息。
哟凯文。 :) 如果我跟进,您当前的工作流程是:
- 让用户提交表单(以创建新帐户)。
- 如果创建新帐户失败,请手动将错误附加到表单。
不好。现在您在两个地方进行了验证:提交表单之前和之后。
您应该使用自定义验证器(就像您在初始代码中所做的那样)。 但是由于检查用户名或电子邮件是否被占用可能会触发 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
之后的代码)。 - 此外,提交表单后设置错误的逻辑也不理想。