Reputation: 12636
My async validator is not working when I actually make an async call (but works when I mock it with Observable.of(result)
. The html
looks like this:
<div class="form-group">
<label for="emailAddress" class="control-label">Email</label>
<input type="email"
id="emailAddress"
class="form-control"
[(ngModel)]="parent.EmailAddress"
name="emailAddress"
[ngModelOptions]="{updateOn: 'blur'}"
appDuplicateEmailValidator
#emailAddress = "ngModel">
</div>
The validator in question is appDuplicateEmailValidator
.
The validation code looks like this:
public validate(control: AbstractControl): Observable<ValidationErrors | null> {
// return observableOf({ 'isDuplicateEmail': true });
return this.checkForDupeUserAndGetValidationState(emailAddress);
}
private checkForDupeUserAndGetValidationState(emailAddress): Observable<ValidationErrors | null> {
const validationStateObs: Observable<ValidationErrors | null> = new Observable(observer => {
this.adminService.getUser(emailAddress).subscribe(
(res: any) => {
const validationState = !!res ? observableOf({ 'isDuplicateEmail': true }) : null;
console.log('Observer next, validation state: ', validationState); // Gets correct val state
observer.next(validationState);
console.log('Is it hitting this'); // Gets hit
}, (err) => {
observer.next(null);
});
});
An important note is that when I uncomment // return observableOf({ 'isDuplicateEmail': true });
, everything works as expected. But the way you see the code above, it doesn't work, even though the observable is returning the correct value (logged it an used debugger). By "it doesn't work" I mean that the form control is never entering an error state, as shown by:
<pre>IS INVALID: {{emailAddress.invalid}}</pre>
<pre>ERRORS: {{emailAddress.errors | json}}</pre>
Why and how do I fix it?
Upvotes: 2
Views: 3947
Reputation: 3083
The question is, why do you subscribe and then return that as new Observable
? I think you should be able to do this:
private checkForDupeUserAndGetValidationState(emailAddress): Observable<ValidationErrors | null> {
return this.adminService.getUser(emailAddress).pipe(map(
(res: any) => {
return !!res ? { 'isDuplicateEmail': true } : null;
}, catchError(err => {
return of(null);
})));
}
This way you will let the async validator itself handle subscription for you...
Hope this helps...
Upvotes: 1
Reputation: 29305
you have 3 issues. 1. you need to actually return your observable 2. you're sending an observable instead of a value through your observer 3. the observable needs to complete.
you could do this:
private checkForDupeUserAndGetValidationState(emailAddress): Observable<ValidationErrors | null> {
const validationStateObs: Observable<ValidationErrors | null> = new Observable(observer => {
this.adminService.getUser(emailAddress).subscribe(
(res: any) => {
const validationState = !!res ? { 'isDuplicateEmail': true } : null; // just send the value
observer.next(validationState);
observer.complete(); //complete here
}, (err) => {
observer.next(null);
observer.complete(); // and complete here
});
});
return validationStateObs; // return it here
}
but way easier and cleaner would just be to return the response from your service and using operators instead of creating an observable and nesting a subscribe inside it..
private checkForDupeUserAndGetValidationState(emailAddress): Observable<ValidationErrors | null> {
return this.adminService.getUser(emailAddress).pipe(
map((res: any) => !!res ? { 'isDuplicateEmail': true } : null),
catchError((err) => observableOf(null))
)
}
http observables naturally complete after one emission, so you don't need to worry about completing or any of the other complex logic you had.
Upvotes: 8
Reputation: 143
observableOf({ 'isDuplicateEmail': true })
is returning an Observable right? you shoud put normal method in observer.next() not an observable.
try removing the observableOf and test it. it should be something like this:
private checkForDupeUserAndGetValidationState(emailAddress): Observable<ValidationErrors | null> {
const validationStateObs: Observable<ValidationErrors | null> = new Observable(observer => {
this.adminService.getUser(emailAddress).subscribe(
(res: any) => {
const validationState = !!res ? { 'isDuplicateEmail': true } : null;
console.log('Observer next, validation state: ', validationState); // Gets correct val state
observer.next(validationState);
console.log('Is it hitting this'); // Gets hit
}, (err) => {
observer.next(null);
});
});
I hope it works this way
Upvotes: 0