0

I am trying to implement a custom validator that checks if the keyed username exists. However, I am running into a problem where the control is always invalid. I have confirmed the webApi is returning the correct values and the validator function is stepping into the proper return statements.

My custom validator is as follows:

import { Directive, forwardRef } from '@angular/core'; import { AbstractControl, ValidatorFn, NG_VALIDATORS, Validator, FormControl } from '@angular/forms'; import { UsersService } from '../_services/index'; import { Users } from '../admin/models/users'; function validateUserNameAvailableFactory(userService: UsersService): ValidatorFn { return (async (c: AbstractControl) => { var user: Users; userService.getUserByName(c.value) .do(u => user = u) .subscribe( data => { console.log("User " + user) if (user) { console.log("Username was found"); return { usernameAvailable: { valid: false } } } else { console.log("Username was not found"); return null; } }, error => { console.log("Username was not found"); return null; }) }) } @Directive({ selector: '[usernameAvailable][ngModel]', providers: [ { provide: NG_VALIDATORS, useExisting: forwardRef(() => UserNameAvailableValidator), multi: true } ] }) export class UserNameAvailableValidator implements Validator { validator: ValidatorFn; constructor(private usersService: UsersService) { } ngOnInit() { this.validator = validateUserNameAvailableFactory(this.usersService); } validate(c: FormControl) { console.log(this.validator(c)); return this.validator(c); } } 

And the form looks like:

<form #userForm="ngForm" (ngSubmit)="onSubmit()" novalidate> <div class="form form-group col-md-12"> <label for="UserName">User Name</label> <input type="text" class="form-control" id="UserName" required usernameAvailable maxlength="50" name="UserName" [(ngModel)]="user.userName" #UserName="ngModel" /> <div [hidden]="UserName.valid || UserName.pristine" class="alert alert-danger"> <p *ngIf="UserName.errors.required">Username is required</p> <p *ngIf="UserName.errors.usernameAvailable">Username is not available</p> <p *ngIf="UserName.errors.maxlength">Username is too long</p> </div> <!--<div [hidden]="UserName.valid || UserName.pristine" class="alert alert-danger">Username is Required</div>--> </div> </form> 

I have followed several tutorials proving this structure works, so I am assuming that I am possibly messing up the return from within the validator function.

Also, is there a way I can present the list of errors (I have tried using li with ngFor, but I get nothing from that)?

1

2 Answers 2

1

So, there was something wrong with using .subscribe the way I was. I found a solution by using the following 2 links:

https://netbasal.com/angular-2-forms-create-async-validator-directive-dd3fd026cb45 http://cuppalabs.github.io/tutorials/how-to-implement-angular2-form-validations/

Now, my validator looks like this:

import { Directive, forwardRef } from '@angular/core'; import { AbstractControl, AsyncValidatorFn, ValidatorFn, NG_VALIDATORS, NG_ASYNC_VALIDATORS, Validator, FormControl } from '@angular/forms'; import { Observable } from "rxjs/Rx"; import { UsersService } from '../_services/index'; import { Users } from '../admin/models/users'; @Directive({ selector: "[usernameAvailable][ngModel], [usernameAvailable][formControlName]", providers: [ { provide: NG_ASYNC_VALIDATORS, useExisting: forwardRef(() => UserNameAvailableValidator), multi: true } ] }) export class UserNameAvailableValidator implements Validator { constructor(private usersService: UsersService) { } validate(c: AbstractControl): Promise<{ [key: string]: any }> | Observable<{ [key: string]: any }> { return this.validateUserNameAvailableFactory(c.value); } validateUserNameAvailableFactory(username: string) { return new Promise(resolve => { this.usersService.getUserByName(username) .subscribe( data => { resolve({ usernameAvailable: true }) }, error => { resolve(null); }) }) } } 

This is now working. However, I now have an issue where the the control temporarily invalidates itself while the async validator is running.

Sign up to request clarification or add additional context in comments.

1 Comment

Maybe keep the field invalidated until the async call completes?
0

Return for true should be:

{usernameAvailable: true}

or null for false.

1 Comment

I was playing around to see if I had messed something up with the returns and realized it has something to do with trying to return a value from within the subscribe. If I hard code the return statement and comment the service call, the error is populated properly. I just don't have the understanding to know what I am doing wrong in the subscribe event.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.