In nested FormGroup the validators on the FormControl are not working

67 views Asked by At

I have a FormGroup that contains a nested DatePicker FormGroup; this nested FormGroup contains two FormControls FromDate and ToDate.

For this nested FormGroup Validators I have two of them. one depends on the each FormControl individually and one depends on the nested FormGroup as a whole.

The issue is that the Validators on the FormControls are not working only the Validator on the FormGroup as a whole is working.

here is a code snippet of my FormGroup

  createForm() {
    this.form = this.formBuilder.group({
      id:[0],
      dateFrom: [, Validators.required],
      dateTo: [, Validators.required],
      tempDateRange: new FormGroup({
        from: new FormControl([, financialYearRangeValidator]),
        to: new FormControl([, financialYearRangeValidator]),
      }, periodRangeValidator),
    });
  }

I think I can make them all in one Validator but I thought it will be better separating them.

1

There are 1 answers

0
Eliseo On

The periodRangeValidator belong to the FormGroup tempDateRange

So, if you're using simples datePickers (or inputs) you have

<div [formGroup]="form">
  ...
  <div formGroupName="tempDateRange">
    <input formControlName="from">
    <input formControlName="to">
    <div *ngIf="form.hasErros('periodInvalid','tempDateRange') &&
                      form.get('tempDateRange)?.touched">
        error!!
    </div>
</div>

If you are using, material-angular, the mat-error is related to the input included in the mat-form-field so you need use a custom-error-matcher

A simple example:

1.-Define a class that implements ErrorStateMatcher

export class ErrorGroupMatcher implements ErrorStateMatcher {
  constructor(private control1: string, private control2: string) {}
  isErrorState(
    control: FormControl | null,
    form: FormGroupDirective | NgForm | null
  ): boolean {
    const parent = control ? control.parent : null;
    if (parent) {
      if (parent.get(this.control1)?.value && parent.get(this.control2)?.value)
        return !!(parent && parent.invalid && parent.touched);
    }
    return false;
  }
}

2.-In the component.ts

//define a formGroup

  form = new FormGroup({
    details: new FormGroup(
      {
        one: new FormControl('', Validators.required),
        two: new FormControl(''),
      },
      this.match()
    ),
  });

//declare an object matcher, see how pass the two fields

  matcher = new ErrorGroupMatcher('one', 'two');

//define a custom validator

  match() {
    return (control: AbstractControl) => {
      const group = control as FormGroup;
      const value1 = group.get('one')?.value || null;
      const value2 = group.get('two')?.value || null;
      return value1 === null || value1 == value2
        ? null
        : { error: 'not match' };
    };
  }

3.-In your .html

<div [formGroup]="form">
  <div formGroupName="details">
    <mat-form-field class="example-full-width">
      <mat-label>One</mat-label>
      <input matInput formControlName="one" />
      <mat-error> required </mat-error>
    </mat-form-field>
    <mat-form-field class="example-full-width">
      <mat-label>two</mat-label>
      <input matInput formControlName="two" [errorStateMatcher]="matcher" />
      <mat-error> one and two should be match! </mat-error>
    </mat-form-field>
  </div>
</div>

See a stackblitz

NOTE: See that it's a bit complex the custom error matcher because I want check if the two fields are not empty or null because if one have a Validators.required, the formGroup is invalid