userx
userx

Reputation: 515

Why custom validation message does not work

too many hours wasted because of this bug. I wrote a custom validation that makes sure that the "currency name" is not repeated, the custom validation function called "validateNameUniqness" is calling a http service from a laravel api.

laravel must determine if the name is repeated or not: if name is not repeated then laravel will return true, else it will return an error message. in both cases laravel will mark the response with the status "200".

laravel code:

 public function validateCurrencyNameUniqness($name){
    $request = new Request();
    $request->replace(['name' => $name]);

    $validator = Validator::make($request->all(),[
      'name' => 'unique:currencies',
      ]);
    if ($validator->fails()) {
        $uniqness_error = $validator->errors()->first('name');
        return response()->json($uniqness_error, 200);
    }
    return response()->json(true, 200);
}

angular service will call the laravel api and return Promise:

 validateCurrencyNameUniqness(name:string): Promise<any>{
   return 
    this.http.get('http://127.0.0.1:8000/api/validateCurrencyNameUniqness/' 
     + name)
     .toPromise()
     .then(this.extractData)
     .catch(this.handleError);
  }

private extractData(res: Response) {
   let body = res.json();
    return body || {};
}

private handleError(error: any): Promise<any> {
    console.error('An error occurred', error);
    return Promise.reject(error.message || error);
}

angular custom validation function will return null if name is not repeated, else it will return an object:

validateNameUniqness(control: FormControl ): {[key: string]: any} { 
  let name = control.value; 
  if(name){
      // if name is valid return null, else return error object
      this.services.validateCurrencyNameUniqness(name)
      .then(result =>{
          if(result !== true){
            this.unqiness_msg = result;
            console.log(this.unqiness_msg );
            return {'not_unique': {value: true}}
            //return {'not_unique': true};
          }

      })
      .catch(error => console.log(error));   
 }
 return null;
 }

angular material & html script:

 <mat-form-field>
            <input type="text" matInput #currency_name maxlength="30" placeholder="Currency Name" required [(ngModel)]="currency.name" name="name" [formControl]="name"   (readystatechange)="validateNameUniqness(name)">
            <mat-hint align="end">{{currency_name.value?.length || 0}}/30</mat-hint>
                <mat-error *ngIf="name.invalid && (name.dirty || name.touched)">{{getErrorMessage()}}</mat-error>
            </mat-form-field>

          <mat-form-field>

the validation call:

 name = new FormControl('', [Validators.required,this.validateNameUniqness.bind(this)]);

 getErrorMessage() {
         if(this.name.hasError('not_unique'))
             return this.unqiness_msg;
         if (this.name.hasError('required') || this.symbol.hasError('required') || this.decimals_after_point.hasError('required'))
             return 'You must enter a value';


     }

I think that the validation form is never be "Invalid" for the uniqueness validation. testing showing that the custom validation function is logging the error message to the console if the name is repeated, but it never show the error message to the user!

any help is appreciated.

Upvotes: 0

Views: 280

Answers (2)

Ricardo
Ricardo

Reputation: 2487

you have some small issues in your validator

  1. your validator should always return
  2. your validator should always return promises
  3. you should inject the service in your function

your code should look like this (this is my custom validator implementation)

export const buidUsermameValidator = (service: UserService, oldUsername?: string) => (username: FormControl): ValidationErrors | null => {
  if (username && username.value !== "" && username.value !== oldUsername) {
    return service.usermameExist(username.value).toPromise()
      .then((result) => {
        if (result) {
          return { "exist": true }
        }
        return null
      })
      .catch(err => { return null })
  } else {
    return Promise.resolve(null)
  }
}

when you going to use your validator your code should look like this

this.userForm = this.fb.group({
      "username": ["", Validators.compose([Validators.required]), buidUsermameValidator(this.service)],

    })

Upvotes: 1

Zlatko
Zlatko

Reputation: 19569

For one, this needs to be an async validator, check that if you didn't do it already. For two, your validator function is not returning anything. Return that promise you get from service.

if (name) {
    // if name is valid return null, else return error object
    return this.services.validateCurrencyNameUniqness(name) 

Also, do not forget to also return from both .then and .catch handlers.

Upvotes: 0

Related Questions