0
votes

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;
    }

  }
2
You need to use map instead of subscribe and return the mapped observable. Also you need to set it as an async validator - Aluan Haddad
I am new to angular could you show the example of how to do this? I have no idea how to use map to be honest. @AluanHaddad - PutML
You can find information about Map here: angular.io/guide/rx-library#operators Basically you want to use .map(sala=> rather than subscribe(sala=> - Rinktacular
@Rinktacular, When I use .map(sala=> I receive error "Property 'map' does not exist on type 'Observable<Sala>" - PutML
Make sure to use this at the top of your file: import { map } from 'rxjs/operators' - Rinktacular

2 Answers

2
votes
  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)
    });
0
votes

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
  // ...
}));