0

Here is the plnkr for my question: https://plnkr.co/edit/ObTq02rzcTdA6I2W

The important code to focus on is:

this.formGroup = this.formBuilder.group({
  dropdown: [null, Validators.required],
  textbox: [null, requiredIf(() => this.formGroup.get('dropdown').value == "isRequired")]
});

saveButtonClick () {
  this.formGroup.updateValueAndValidity();
  console.log("form valid: " + this.formGroup.valid);
}

requiredIf (predicate) {
    return ((formControl) => {
        if (predicate()) {
            return Validators.required
        }
        return null;
    });
}

If you open up the console while running the example code you can see which functions are being called. The predicate passed to requiredIf is only called when the value of the text box changes. I expected it to be called when any field changes, or at least when saveButtonClick() calls updateValueAndValidity() but neither of those things are happening.

How can I trigger the conditional validation to be reevaluated when the save button is clicked?

I would strongly prefer to not subscribe to the value changes event on the dropdown. I'm working with a large form with a lot of conditional validation and it would be best if the code I have now is all that we needed to achieve the desired effect.

Muuski
  • 198
  • 1
  • 10

2 Answers2

0

After some testing I eventually found that updateValueAndValidity does not, as I assumed, update all the controls under the form and that by iterating through all the controls and updating each one individually we could achieve the desired result:

  recursivelyUpdateAndValidate(control) {
    if (control.controls) {
      Object.keys(control.controls).forEach(key => {
          this.recursivelyUpdateAndValidate(control.get(key));
        });
    }
    control.updateValueAndValidity();
  }

  saveButtonClick() {
      this.recursivelyUpdateAndValidate(this.formGroup);
      console.log("form valid: " + this.formGroup.valid);
    }
  }

I don't know if this is the best way to resolve the issue or if I'm going to find another problem with this but it appears to be working so far.

The reason I used recursion here is because it failed when there was nesting in the form, like so:

this.formGroup = this.formBuilder.group({
  dropdown: [null, Validators.required],
  textbox: [null, textboxValidator],
  nested: this.formBuilder.group({
    anotherTextBox: [null, textboxValidator],
  })
});

I will leave this question open in case better answers come around.

Muuski
  • 198
  • 1
  • 10
0

for me, a good aproach is disable/enable the control (when a FormControl is disabled always is valid), see, e.g. this SO question

In the SO is used a checkbox external to the form, you can use some like

<input formControlName="textbox" 
     [enabledControl]="form.get('dropdown').value=='isRequired'">

NOTE: If you want hide the input NOT use *ngIf use

[style.display]="form.get('dropdown').value=='isRequired'?'block':'none'
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • Thing is that I want the field to still be editable when it is not required. Think of for example a feedback form, the comment field may only be required if you're reporting an issue, but you can still leave an optional comment if you're not. – Muuski Apr 14 '20 at 15:55
  • ::glups::, then, obviously mine, it's a bad idea. You can make a validator over all the form, but this make, each change in any input goes to the validator. So, the only thing I imagine is has two validators, one of the text, and another one of the check. You show the error only under the text – Eliseo Apr 14 '20 at 16:01