2

I have a register form where user need to provide username. When customer enters username, I want to show validation error message if that username already exists in db or not.

register.html

 <-- code here-->
<div class="form-group">
            <label for="username" class="col-sm-3 control-label">UserName</label>
            <div class=" col-sm-6">
             <input type="text" ngControl="userName" maxlength="45" class="form-control" [(ngModel)]="parent.userName" placeholder="UserName" #userName="ngForm" required data-is-unique/>
                <validation-message control="userName"></validation-message>
            </div>
        </div>
 <--code here-->

register.component.ts

import {Component} from 'angular2/core';
import {NgForm, FormBuilder, Validators, FORM_DIRECTIVES} from  'angular2/common';
   import {ValidationService} from '../services/validation.service';
  import {ValidationMessages} from './validation-messages.component';
  @Component({
    selector: 'register',
    templateUrl: './views/register.html',
    directives: [ROUTER_DIRECTIVES, ValidationMessages, FORM_DIRECTIVES],
    providers: []
   })
  export class ParentSignUpComponent {
   parentSignUpForm: any;
   constructor(private _formBuilder: FormBuilder) {
    this._stateService.isAuthenticatedEvent.subscribe(value => {
        this.onAuthenticationEvent(value);
    });
    this.parent = new ParentSignUpModel();
    this.parentSignUpForm = this._formBuilder.group({
        'firstName': ['', Validators.compose([Validators.required, Validators.maxLength(45), ValidationService.nameValidator])],
        'middleName': ['', Validators.compose([Validators.maxLength(45), ValidationService.nameValidator])],
        'lastName': ['', Validators.compose([Validators.required, Validators.maxLength(45), ValidationService.nameValidator])],
        'userName': ['', Validators.compose([Validators.required, ValidationService.checkUserName])]
    });
}
}

validation-message.component

import {Component, Host} from 'angular2/core';
import {NgFormModel} from 'angular2/common';
import {ValidationService} from '../services/validation.service';

@Component({
   selector: 'validation-message',
   inputs: ['validationName: control'],
   template: `<div *ngIf="errorMessage !== null" class="error-message"> {{errorMessage}}</div>`
    })
     export class ValidationMessages {
    private validationName: string;
    constructor (@Host() private _formDir: NgFormModel) {}
    get errorMessage() {
    let control = this._formDir.form.find(this.validationName);
    for (let propertyName in control.errors) {
        if (control.errors.hasOwnProperty(propertyName) && control.touched)   {
            return ValidationService.getValidatorErrorMessage(propertyName);
        }
      }
    return null;
  }
 }

validation-service.ts

  import {Injectable, Injector} from 'angular2/core';
  import {Control} from 'angular2/common';
  import {Observable} from 'rxjs/Observable';
  import {Http, Response, HTTP_PROVIDERS} from 'angular2/http';
  import 'rxjs/Rx';       
  interface ValidationResult {
    [key:string]:boolean;
    }
 @Injectable()
 export class ValidationService {
   static getValidatorErrorMessage(code: string) {
    let config = {
      'required': 'This field is required!',
      'maxLength': 'Field is too long!',
      'invalidName': 'This field can contain only alphabets, space, dot, hyphen, and apostrophe.',
      'userAlreadyInUse': 'UserName selected already in use! Please try another.'
};
return config[code];
}
static checkUserName(control: Control): Promise<ValidationResult> {
    let injector = Injector.resolveAndCreate([HTTP_PROVIDERS]);
    let http = injector.get(Http);
    let alreadyExists: boolean;
    if (control.value) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            http.get('/isUserNameUnique/' + control.value).map(response => response.json()).subscribe(result => {
                if (result === false) {
                    resolve({'userAlreadyInUse': true});
                } else {
                    resolve(null);
                }
            });
        }, 1000);
    });
    }
}
 }

Now, when i run, and give a username that already exists in db, the value of 'result' variable i am getting as false, which is expected and correct. But validation error message is not getting displayed. I am able to run and get validation error message for other custom validation functions. I am using Angular 2.0.0-beta.15. Can somebody help me to understand what could be the issue?

2 Answers 2

1

There are some known issues with async validation


This code can be simplified

  return new Promise((resolve, reject) => {
    setTimeout(() => {
      http.get('/isUserNameUnique/' + control.value).map(response => response.json())
      .subscribe(result => {
        if (result === false) {
          resolve({'userAlreadyInUse': true});
        } else {
          resolve(null);
        }
      });
    }, 1000);
  });

to

  return http.get('/isUserNameUnique/' + control.value).map(response => response.json())
  .timeout(200, new Error('Timeout has occurred.'));
  .map(result => {
    if (result === false) {
      resolve({'userAlreadyInUse': true});
    } else {
      resolve(null);
    }
  }).toPromise();

Don't forget to import map, timeout, and toPromise.

If you use subscribe() instead of then() on the caller site, then you can event omit toPromise()

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

4 Comments

return http.get('/isUserNameUnique/' + control.value).map(response => response.json()) .timeout(200, new Error('Timeout has occurred.')); .subscribe(result => { if (result === false) { resolve({'userAlreadyInUse': true}); } else { resolve(null); } }); did u mean this? because i cannot see then or subscribe in your piece of code. Thanks for letting me know more about this issue. I was going through links and couldnt get any hint.
then or subscribe needs to be used by the caller of checkUserName checkUserName(c).then(...) or checkUserName(c).subscribe(...). This is what Angular forms do for async validators (I don't know if async validators require to return a Promise or if they also work with Observable)
My problem got solved. Its not because of that checkUserName method. if you look into this - 'userName': ['', Validators.compose([Validators.required, ValidationService.checkUserName])] }); -- you can see I am using both synchronous and asynchronous validations together. When i changed method for checkUserName like 'Validators.composeAsync(ValidationService.checkUserName)' instead of Validators.compose method, error message got displayed. :)
Great, thanks for the feedback. Just post this as an answer and accept it to make it obvious this question is answered and solved.
0

if you look into this -

'userName': ['', Validators.compose([Validators.required, ValidationService.checkUserName])] });

-- you can see I am using both synchronous and asynchronous validations together. When i changed method for checkUserName like 'Validators.composeAsync(ValidationService.checkUserName)' instead of Validators.compose method, error message got displayed.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.