Reputation: 336
The idea is to let the user POST the form. And trigger the error returned by the API for set the email field as error if the user is already register.
I use reactive forms with FormBuilder and I trying calling the validator in the subscribe error catcher :
Constructor :
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
});
Custom alreadyExist() validator :
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;
});
It appear that the validator is called but the returned anonymous method inside it is never called... Maybe isn't a good practice, anyone can highlight me ? thx
Upvotes: 2
Views: 5515
Reputation: 14087
Yo Kévin. :) If I followed, your current workflow is:
NOT GOOD. Now you have validation in two places: BEFORE and AFTER form submission.
You should use a custom validator (like you did in your initial code). But since checking whether a username or email is taken will likely trigger an HTTP call, you must use an asynchronous validator.
In the form declaration, async validators appear AFTER synchronous validators:
this.userForm = fb.group({
// `userExists` is in 3rd position, it's an async validator
username: ['', Validators.required, userExists],
password: []
});
Now the code for userExists
. Since it is an async validator, it must return an observable or a promise:
// 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();
});
}
Play with the code in this Plunkr: https://plnkr.co/edit/aPtrp9trtmwUJDJHor6G?p=preview — Try to enter an existing username ('kevin', 'vince', or 'bernard') and you'll see an error message BEFORE you even submit the form.
Why wasn't your code working? I see several errors:
alreadyExist()
, only keep the code after the return
).Upvotes: 2
Reputation: 336
Ok I found a good solution.
In my case, for the email FormControl, I don't need a custom validator (even if is possible with setValidators()).
I can delete alreadyExist() and remove its declaration from the list of validators.
Then, I use setErrors() method available on FormControl :
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;
});
The email FormControl has a new error, so, by this way i can attach a error message for this error.
Upvotes: 7