PutML
PutML

Reputation: 75

Angular custom validator is not throwing an error when using subscribe in it

I am trying to create custom validator which needs to hit two endpoint to fetch needed data and then compare properties of the data and if they are not equal then I want to throw an error. Here is the validator.

 checkIfSalaIsFull(control: AbstractControl): {[salaIsFull: string] : boolean}{
    if(control.value !=null){
      this.salaService.findActiveCards(control.value).subscribe(karty=>{
        this.salaService.findSalaByNRSali(control.value).subscribe(sala =>{
          if(karty.length === sala.pojemnosc){
            console.log(karty.length);
            console.log(sala.pojemnosc);
            return {'salaIsFull' : true};
          }
        });
      });
    }
    return null;
  }

The problem is that error is not thrown even though console.log show the same value for showed properties.

<div class="form-group">
    <label for="nr_sali">Numer sali</label>
    <select class="form-control" id="nr_sali" formControlName="nr_sali">
      <option *ngFor="let sala of sale" [value]="sala.nr_sali" >{{sala.nr_sali}} - {{sala.oddzial}}</option>

    </select>

    <span *ngIf="formAddKarta.get('nr_sali').errors?.salaIsFull"
          class="help-block text-danger">
          Sala jest pełna
    </span>
</div>

My backend is working, I checked it with postman.

Here is also a setup of FormGroup which I am using:

 this.formAddKarta = new FormGroup({
      data_przyjecia: new FormControl(null, [KartyListaComponent.checkIfDateIsLessThanToday.bind(this),
        Validators.required]),
      godzina_przyjecia: new FormControl(null,[Validators.required]),
      data_wypisu: new FormControl(null),
      nr_sali: new FormControl(null, [Validators.required,this.checkIfSalaIsFull.bind(this)]),
      pesel: new FormControl(this.pesel)
    });

I set this custom validator in Validators table in nr_sali FormControl.

EDIT:

I tried to use map instead of subscribe but then it is not sending request to retrieve sala, only sending request to retreive karty.

 checkIfSalaIsFull(control: AbstractControl): {[salaIsFull: string] : boolean}{
    if(control.value !=null){
      this.salaService.findActiveCards(control.value).subscribe(karty=>{
        this.salaService.findSalaByNRSali(control.value).pipe(map(sala =>{
          if(karty.length === sala.pojemnosc){
            console.log(karty.length);
            console.log(sala.pojemnosc);
            return {'salaIsFull' : true};
          }else{
            return null;
          }
        }));
      });
    }else{
      return null;
    }

  }

Upvotes: 0

Views: 240

Answers (2)

Ashot Aleqsanyan
Ashot Aleqsanyan

Reputation: 4453

  1. you need to use async validator instead of validator, because you need to send request, and you need to put your function as third parameter in your formControl (as Async validator)
  2. your validator function should be return Observable<ValidationErrors | null> | Promise<ValidationErrors | null>
  3. you need to use switchMap and map instead of subscription

here is the code example

imports

import { map, switchMap } from "rxjs/operators";
import { Observable } from "rxjs/internal/observable";

async validator example

  checkIfSalaIsFull(
    control: AbstractControl
  ): Observable<ValidationErrors | null> {
    if (control.value != null) {
      return this.salaService.findActiveCards(control.value).pipe(
        switchMap(karty => {
          return this.salaService.findSalaByNRSali(control.value).pipe(
            map(sala => {
                console.log(karty.length);
                console.log(sala.pojemnosc);
                return karty.length === sala.pojemnosc ? { salaIsFull: true } : null;

            })
          );
        })
      );
    }
    return null;
  }

Form Implementation example

 this.formAddKarta = new FormGroup({
      data_przyjecia: new FormControl(null, [
KartyListaComponent.checkIfDateIsLessThanToday.bind(this),  /** <-- if this function send the request you need to change this one too */

        Validators.required]),
      godzina_przyjecia: new FormControl(null, [Validators.required] ),
      data_wypisu: new FormControl(null),
      nr_sali: new FormControl(null, 
[Validators.required],
 [this.checkIfSalaIsFull.bind(this)]),  /** <-- third argument of formControl is the async validators array */
      pesel: new FormControl(this.pesel)
    });

Upvotes: 2

Rinktacular
Rinktacular

Reputation: 1136

Continuing from my above comments, you want to use map instead of subscribe. Since Angular 6, rxjs (the library map comes from) wraps requires it's operations to be wrapped in a pipe function like so:

import { map } from 'rxjs/operations'

...

this.salaService.findSalaByNRSali(control.value).pipe(map(sala => { 
  // Do work
  // ...
}));

Upvotes: 0

Related Questions