Angular - custom validator does not use latest value of component variable

49 views Asked by At

I have the following form group that gets created in ngOnInit:

  private setScreeningStatusForm(screeningStatusData: ClientScreeningStatusModel | undefined): void {
    this.screeningStatusForm = new FormGroup({
      field_screening_status_select: new FormControl(screeningStatusData?.status, [Validators.required, this.screeningStatusValidator().bind(this)]),
      field_screening_status_comment: new FormControl(screeningStatusData?.userComment)
    });
  }

I have the following validator that ensures the user cannot save the same option (where this.screeningStatus is the currently saved option and gets updated when saving):

  private screeningStatusValidator(): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      return this.screeningStatus?.status === control.value ? { 'invalidStatusSelection': true } : null;
    };
  }

I'm running into an issue whereby when validating, the validator uses the original value of this.screeningStatus rather than the latest value. As you can see in the code, I've tried binding this to the validator to get the latest value but no dice.

Do I have to recreate the form group or remove/re-add the validator everytime this.screeningStatus changes? I have a working solution where I am doing this in the set of the this.screeningStatus:

  public set screeningStatus(value: ClientScreeningStatusModel | undefined) {
    this._screeningStatus = value;
    this.setScreeningStatusForm(value);
  }

and this works without needing to bind, however it is clunky and I'd like the bind to work. What am I doing wrong?

1

There are 1 answers

2
Adrien On

If I understand correctly, you want to make sure the next status is not the same as the one alreadySet.

If you want the data to be updated each type there is a change, you could use a BahaviourSuject for that.

 this._screeningStatus = new BehaviorSubject(this.cleaningStatus); // set the initalValue of the status

Then, you should create only once the form.

Create your form in the ngOnInit function with the screeningStatus validator and the behaviour subject as a parameter;

ngOnInit(): void {
  this.screeningStatusForm = new FormGroup({
      field_screening_status_select: new FormControl(undefined, [Validators.required, this.screeningStatusValidator(this._screeningStatus)]),
      field_screening_status_comment: new FormControl()
    });
}

And then, you should modify your setter to set the values to the form

public set screeningStatus(value: ClientScreeningStatusModel | undefined) {
   this.screeningStatusForm.controls.field_screening_status_select.setValue(value?.status);
   this.screeningStatusForm.controls.field_screening_status_comment.setValue(value?.userComment;

  // This needs to be after, to be sure not to test the same value
  this._screeningStatus.next(value);
}

And adapt the validator as such

private screeningStatusValidator(screeningStatus: BehaviorSubject<ClientScreeningStatusModel>): ValidatorFn {
    return (control: AbstractControl): ValidationErrors | null => {
      return screeningStatus.value.status === control.value ? { 'invalidStatusSelection': true } : null;
    };
  }