hofshteyn
hofshteyn

Reputation: 1272

Reactive forms: mark dirty all controls in form group

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

Upvotes: 17

Views: 27676

Answers (8)

Andrea Moretti
Andrea Moretti

Reputation: 63

I will post update for who is using Angular18+ based on @Sachin Gupta answer. His solution wasn't working for me till I found out constructor.name on FormControl was returning FormControl2 (idk why). So this is my util class:

import { AbstractControl, FormArray, FormControl, FormGroup } from "@angular/forms";

export class FMWFormControlUtils {
    static isValid(control: AbstractControl) {
        return control.valid
            && !control.pristine
            && (control.touched)
            && control.status != 'DISABLED'
            && control.status != 'PENDING';
    }

    static markGroupDirty(formGroup: FormGroup) {
        Object.keys(formGroup.controls).forEach((key) => {
            const name = formGroup.get(key)!.constructor.name;
            if (name.includes('FormGroup')) {
                this.markGroupDirty(formGroup.get(key) as FormGroup);
            }
            if (name.includes('FormArray')) {
                this.markArrayDirty(formGroup.get(key) as FormArray);
            }
            if (name.includes('FormControl')) {
                this.markControlDirty(formGroup.get(key) as FormControl);
            }
        });
    }
    static markArrayDirty(formArray: FormArray) {
        formArray.controls.forEach((control) => {
            const name = control.constructor.name
            if (name.includes('FormGroup')) {
                this.markGroupDirty(control as FormGroup);
            }
            if (name.includes('FormArray')) {
                this.markArrayDirty(control as FormArray);
            }
            if (name.includes('FormControl')) {
                this.markControlDirty(control as FormControl);
            }
        });
    }
    static markControlDirty(formControl: FormControl) {
        formControl.markAsDirty();
    }
}

Upvotes: 0

Roberto Zvjerković
Roberto Zvjerković

Reputation: 10127

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();
});

Upvotes: 14

Sachin Gupta
Sachin Gupta

Reputation: 5301

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();
}

Upvotes: 14

Iulian Tofan
Iulian Tofan

Reputation: 31

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]);

Upvotes: 3

Ron Jonk
Ron Jonk

Reputation: 901

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');
  }
}

Upvotes: 1

Nicolas Ay
Nicolas Ay

Reputation: 71

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));

Upvotes: 5

Shogg
Shogg

Reputation: 841

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);
}

Upvotes: 1

Abhishek Gautam
Abhishek Gautam

Reputation: 1767

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();
        });

Upvotes: 3

Related Questions