Reactive forms: mark dirty all controls in form group
Asked Answered
A

7

17

I have Angular 6 app with form. Here is an examle

export class ExampleComponent implements OnInit {
    form: FormGroup;

    constructor(private fb: FormBuilder) { }

    ngOnInit() {
        this.form = new FormGroup({
            first: new FormControl(),
            last: new FormControl()
        });

        this.markControlsAsDirty(this.form);
    }

    markControlsAsDirty(form: FormGroup) {
        this.form.get('first').markAsDirty();
        this.form.get('last').markAsDirty();
    }
}

I don't want to get a single control and mark every field. Can I mark all controls in form group as dirty?

UPDATE I've been added stackblitz example to show that two previous answers were wrong

Agenesis answered 1/3, 2019 at 10:59 Comment(0)
C
13

If you have a complicated form structure, you can segregate the code to mark FormGroup, FormArray or FormControl as dirty. See the example here : Mark Form as dirty

markDirty() {
  this.markGroupDirty(this.form);
  console.log('FORM:', this.form);
}
markGroupDirty(formGroup: FormGroup) {
  Object.keys(formGroup.controls).forEach((key) => {
    switch (formGroup.get(key).constructor.name) {
      case 'FormGroup':
        this.markGroupDirty(formGroup.get(key) as FormGroup);
        break;
      case 'FormArray':
        this.markArrayDirty(formGroup.get(key) as FormArray);
        break;
      case 'FormControl':
        this.markControlDirty(formGroup.get(key) as FormControl);
        break;
    }
  });
}
markArrayDirty(formArray: FormArray) {
  formArray.controls.forEach((control) => {
    switch (control.constructor.name) {
      case 'FormGroup':
        this.markGroupDirty(control as FormGroup);
        break;
      case 'FormArray':
        this.markArrayDirty(control as FormArray);
        break;
      case 'FormControl':
        this.markControlDirty(control as FormControl);
        break;
    }
  });
}
markControlDirty(formControl: FormControl) {
  formControl.markAsDirty();
}
Campbellbannerman answered 1/3, 2019 at 12:16 Comment(1)
Good solution, though I do want to point out that in a production build, the constructor name is going to change/be uglified. Better to do an if/else if block where you check if it's an instaceof of the class.Rech
H
13

Either mark the whole formGroup as dirty:

this.form.markAsDirty();

Or mark each field:

Object.keys(this.form.controls).forEach(key => {
  this.form.get(key).markAsDirty();
});
Hectometer answered 1/3, 2019 at 11:5 Comment(5)
1 option: controls still pristine but form is dirty 2 option: linter error "control does not exist on type Abstract control"Agenesis
1 option: controls still pristine but form is dirty That's literally what you asked for. 2 option: linter error "control does not exist on type Abstract control" Add if (control instanceof FormControl)Stowell
I asked Can I mark all controls in form group as dirty?. Result is not that I expectedAgenesis
I added example in stackblitz, you can see in questions update.Agenesis
Updated the answer.Stowell
C
13

If you have a complicated form structure, you can segregate the code to mark FormGroup, FormArray or FormControl as dirty. See the example here : Mark Form as dirty

markDirty() {
  this.markGroupDirty(this.form);
  console.log('FORM:', this.form);
}
markGroupDirty(formGroup: FormGroup) {
  Object.keys(formGroup.controls).forEach((key) => {
    switch (formGroup.get(key).constructor.name) {
      case 'FormGroup':
        this.markGroupDirty(formGroup.get(key) as FormGroup);
        break;
      case 'FormArray':
        this.markArrayDirty(formGroup.get(key) as FormArray);
        break;
      case 'FormControl':
        this.markControlDirty(formGroup.get(key) as FormControl);
        break;
    }
  });
}
markArrayDirty(formArray: FormArray) {
  formArray.controls.forEach((control) => {
    switch (control.constructor.name) {
      case 'FormGroup':
        this.markGroupDirty(control as FormGroup);
        break;
      case 'FormArray':
        this.markArrayDirty(control as FormArray);
        break;
      case 'FormControl':
        this.markControlDirty(control as FormControl);
        break;
    }
  });
}
markControlDirty(formControl: FormControl) {
  formControl.markAsDirty();
}
Campbellbannerman answered 1/3, 2019 at 12:16 Comment(1)
Good solution, though I do want to point out that in a production build, the constructor name is going to change/be uglified. Better to do an if/else if block where you check if it's an instaceof of the class.Rech
M
5

A simplified version of Sachin Gupta's solution:

public static markAllControlsAsDirty(abstractControls: AbstractControl[]): void {
    abstractControls.forEach(abstractControl => {
      if (abstractControl instanceof FormControl) {
        (abstractControl as FormControl).markAsDirty({onlySelf: true});
      } else if (abstractControl instanceof FormGroup) {
        this.markAllControlsAsDirty(Object.values((abstractControl as FormGroup).controls));
      } else if (abstractControl instanceof FormArray) {
        this.markAllControlsAsDirty((abstractControl as FormArray).controls);
      }
    });
  }

And use it like this:

FormUtils.markAllControlsAsDirty(Object.values(this.form.controls));
Mombasa answered 5/5, 2021 at 13:25 Comment(0)
G
3

Best approach to do :

this will make every control dirty: this.form.markAsDirty();

Use this way (second option):

let controls = this.form.controls;

    controls.forEach(control => {
          this.form.get(control).markAsDirty();
        });
Gangue answered 1/3, 2019 at 11:27 Comment(1)
controls still not dirty but form doesAgenesis
T
3

A even-better version of Nicolas Ay's solution:

Create a utils file for forms and export from there Nicolas's solution

/**
 * Marks all the controls and their nested controls as dirty.
 * @param abstractControls - an array of controls(can be FormControls, FormGroups or FormArrays)
 */
export function markAllControlsAsDirty(abstractControls: AbstractControl[]): void {
  abstractControls.forEach((abstractControl) => {
    if (abstractControl instanceof FormControl) {
      (abstractControl as FormControl).markAsDirty({ onlySelf: true });
    } else if (abstractControl instanceof FormGroup) {
      markAllControlsAsDirty(
        Object.values((abstractControl as FormGroup).controls)
      );
    } else if (abstractControl instanceof FormArray) {
      markAllControlsAsDirty((abstractControl as FormArray).controls);
    }
  });
}

After that you can just call the function anywhere in the app like this:

markAllControlsAsDirty([form]);
Tonitonia answered 21/9, 2022 at 15:58 Comment(1)
Working well indeed +1Mandelbaum
B
1

mark as dirty controls(only those with value) inside FormGroup

markDirtyAllControlsWithValue(form: FormGroup): void {
  const recursiveFunc = (formGroup: FormGroup) => {
    Object.keys(formGroup.controls).forEach(field => {
      const control = formGroup.get(field);
      if (control.value !== null && control.value !== undefined && control.value !== '') {
        control.markAsDirty();
      }
      if (control instanceof FormGroup) {
        recursiveFunc(control);
      }
    });
  };
  recursiveFunc(form);
}
Bobseine answered 5/5, 2020 at 12:7 Comment(0)
S
1

The current version (12) has markAllAsTouched but not markAllAsDirty on the abstractControl.

This is my example that sets dirty to all formControls:

export function markAllAbstractDirty<T extends AbstractControl>(control: T): void {
  if (control instanceof FormGroup) {
    const controls = control.controls;

    Object.keys(controls).forEach(key => {
      controls[key].markAsDirty();
      markAllAbstractDirty(controls[key]);
    });
  } else if (control instanceof FormArray) {
    control.controls.forEach(formControl => formControl.markAsDirty());
  } else if (control instanceof FormControl) {
    control.markAsDirty();
  } else {
    throw new Error('Error: unexpected control value');
  }
}
Sirius answered 21/10, 2021 at 15:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.