0
votes

I have one custom validation to check if a number is in range and one if a number is already assigned.

Code (is number in range)

validateTagIdRange(control: AbstractControl) {
// tagId between 10000 and 39999
  return ((control.value >= 10000) && (control.value <= 39999)) ? null : { tagIdRange: true };
}

Code (is number already assigned)

validateTagId(control: AbstractControl) {
  // is tagId already given?
  let result: number;
  this.userService.getAll().subscribe(
    res => result = res.findIndex(obj => obj['tagId'] === control.value)
  );
  return (result === -1) ? null : { tagIdExists: true };
}

Code (add custom validators to formcontrol)

  this.transponderForm = this.fb.group({
    tagId: ['', [this.validateTagIdRange.bind(this), this.validateTagId.bind(this)]],
  });

Code (in the template)

  <mat-form-field class="example-full-width">
    <input matInput placeholder="Transponder-ID" type="number" formControlName="tagId">
    <mat-error *ngIf="transponderForm.controls.tagId.errors?.tagIdRange">Transponder ID must be between 10.000 and 39.999</mat-error>
    <mat-error *ngIf="transponderForm.controls.tagId.errors?.tagIdExists">Transponder ID is already assigned.</mat-error>
  </mat-form-field>

When an ID is already assigned only the message from tagIdExists should be shown and when the ID is not in range only the message from tagIdRange should be shown. How is it possible to show the correct error message?


The validator TagIdInRange is now working and shows his error message in the form. For the second validator, who checks if a tagId is already taken, it doesn't show the relevant error message.

FormComponent.ts

ngOnInit() {
  this.transponderForm = this.fb.group({
    tagId: ['', Validators.compose([this.validateTagIdRange(), this.validateTagId()])]
});

validateTagIdRange(): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    return ((control.value >= 10000) && (control.value <= 39999)) ? null : { tagIdInRange: true };
  };
}

validateTagId(): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    return this.userService.getAll()
      .map(res => (res.findIndex(obj => obj['tagId'] === result) === -1) ? null : { tagIdExists: true });
  };
}

FormComponent.html

<form [formGroup]="transponderForm">
  <mat-form-field class="example-full-width">
    <input matInput placeholder="Transponder-ID" type="number" formControlName="tagId">
    <mat-error *ngIf="transponderForm.get('tagId').hasError('tagIdInRange')">Die Transponder ID muss im Bereich 10.000 und 39.999 liegen.</mat-error>
    <mat-error *ngIf="transponderForm.get('tagId').hasError('tagIdExists')">Die Transponder ID ist bereits vorhanden.</mat-error>
  </mat-form-field>
</form>

In the app.module.ts ReactiveFormsModule is imported.

1

1 Answers

0
votes

Don't subscribe in your validators this way or you will cause memory leaks. In forms its better to use the Smart/Dumb Components pattern and send your observable as @input to your Dumb Components Explanation of pattern.

In your FormGroup use the Validators.compose and pass in your taglist that was inputted in the Dumb Component

this.transponderForm = this.fb.group({
      'tagId': ['', Validators.compose([this.validateTagIdRange(), this.validateTagId(this.taglist )])]
  });

your validation function then would look like so:

 validateTagId(taglist: any[]): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      const inputValue = control.value;
      for(let i= 0; i < taglist.length; i++) {
         if(obj['tagId'] === control.value) {
             retun { tagIdExists: true };
         }
      }
      return null;
    };
  }

This is how you would print your error

<mat-error *ngIf="form.get('tagId').hasError('tagIdRange')">Range</mat-error>