Set the reactive forms to mark all controls within the form group as dirty

My Angular 6 application has a form with the following example code:

export class SampleComponent 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 am looking for a way to mark all controls in a form group as dirty, rather than having to individually mark each one.

UPDATE: I have added a Stackblitz example to demonstrate that previous answers were incorrect.

Answer №1

If your form structure is complex, you can organize the code to flag FormGroup, FormArray or FormControl as dirty. Check out this example for reference: Making Form Dirty

flagDirty() {
  this.flagGroupDirty(this.form);
  console.log('FORM:', this.form);
}
flagGroupDirty(formGroup: FormGroup) {
  Object.keys(formGroup.controls).forEach((key) => {
    switch (formGroup.get(key).constructor.name) {
      case 'FormGroup':
        this.flagGroupDirty(formGroup.get(key) as FormGroup);
        break;
      case 'FormArray':
        this.flagArrayDirty(formGroup.get(key) as FormArray);
        break;
      case 'FormControl':
        this.flagControlDirty(formGroup.get(key) as FormControl);
        break;
    }
  });
}
flagArrayDirty(formArray: FormArray) {
  formArray.controls.forEach((control) => {
    switch (control.constructor.name) {
      case 'FormGroup':
        this.flagGroupDirty(control as FormGroup);
        break;
      case 'FormArray':
        this.flagArrayDirty(control as FormArray);
        break;
      case 'FormControl':
        this.flagControlDirty(control as FormControl);
        break;
    }
  });
}
flagControlDirty(formControl: FormControl) {
  formControl.markAsDirty();
}

Answer №2

To ensure all fields are marked dirty, you have two options:

this.form.markAsDirty();

Alternatively, you can mark each individual field using the following code snippet:

Object.keys(this.form.controls).forEach(key => {
  this.form.get(key).markAsDirty();
});

Answer №3

A more concise way to make all form controls dirty, inspired by Sachin Gupta:

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

To implement it, simply call:

FormUtils.markAllControlsAsDirty(Object.values(this.form.controls));

Answer №4

Optimal method to follow :

To ensure all controls are marked as dirty: this.form.markAsDirty();

An alternative way to achieve this (second option):

let controlList = this.form.controls;

    controlList.forEach(control => {
          this.form.get(control).markAsDirty();
        });

Answer №5

An enhanced version of Nicolas Ay's approach:

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

/**
 * Sets all controls and their nested controls as dirty.
 * @param abstractControls - an array of controls (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);
    }
  });
}

You can now easily utilize this function within the application by simply calling it like so:

markAllControlsAsDirty([form]);

Answer №6

Identify and mark dirty controls (those with values) within a FormGroup

function markDirtyControlsWithValue(form: FormGroup): void {
  const findAndMark = (group: FormGroup) => {
    Object.keys(group.controls).forEach(key => {
      const control = group.get(key);
      if (control.value !== null && control.value !== undefined && control.value !== '') {
        control.markAsDirty();
      }
      if (control instanceof FormGroup) {
        findAndMark(control);
      }
    });
  };
  findAndMark(form);
}

Answer №7

The current version (12) includes a method called markAllAsTouched, but it does not have a similar method for marking all controls as dirty on the abstractControl.

Here is an example function that sets the dirty state for all formControls:

function setAllControlsDirty<T extends AbstractControl>(control: T): void {
  if (control instanceof FormGroup) {
    const controls = control.controls;
    
    Object.keys(controls).forEach(key => {
      controls[key].markAsDirty();
      setAllControlsDirty(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 type');
  }
}

Answer №8

For those utilizing Angular18+ and looking for an update based on @Sachin Gupta's answer, I encountered a situation where his solution did not work for me initially. It turns out that constructor.name on FormControl was returning FormControl2 for some unknown reason. To address this issue, I created a utility 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();
    }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Dealing with GraphQL mutation errors without relying on the Apollo onError() function

When managing access to an API call server-side, I am throwing a 403 Forbidden error. While trying to catch the GraphQL error for a mutation, I experimented with various methods. (Method #1 successfully catches errors for useQuery()) const [m, { error }] ...

TS2531: Nullability detected in object when using .match() method

I'm encountering a linting error on fileNameMatches[0] in the following code snippet. Strangely, the error doesn't appear on the Boolean() check. Even if I remove that check, the issue remains unresolved. Can anyone suggest a solution? protected ...

transferring information seamlessly in Ionic using Angular router without the need for navigation

I have a list of names that are displayed, and when I click on any name in the list, it should take me to another page without actually navigating to that path. names.page.html: <ion-content> <ion-list *ngFor='let person of people'&g ...

Creating intricate mazes using canvas drawing techniques

I recently developed a maze generator as a personal project utilizing a graph. While the generation logic works perfectly, I am facing challenges when it comes to rendering the maze. In my approach, each cell is represented by an array of 4 edges where the ...

Switching themes on Angular's ag-grid while maintaining infinite scrolling capabilities

In my Angular 9 application, I have successfully implemented an Ag-grid with infinite scroll. Now, I am looking to add a theme chooser similar to the one showcased in the demo. Simply changing the CSS class does not update the row heights when switching t ...

Module 'csstype' not found

I am diving into the world of React with TypeScript. After setting up react and react-dom, I also installed @types/react and @types/react-dom which all went smoothly. However, a pesky error message keeps popping up: ERROR in [at-loader] ./node_modules/@t ...

Node.js encountering an error caused by a lengthy SQL procedure execution, triggered by a POST request initiated from

Currently, my website utilizes Angular, NodeJs, and a SQL database. Most of the site's calls are made from the frontend to the backend and everything runs smoothly. However, I encountered an issue when running a stored procedure that had a longer proc ...

The most effective method for transferring a JavaScript object between a TypeScript frontend and a Node.js backend

I need some advice on how to effectively share a JavaScript object between my Angular2 with Typescript frontend and NodeJS backend in an application I'm working on. Currently, I am using a .d.ts file for the frontend and adding a module.exports in the ...

What could be the root of the error message "Outlet is not activated"?

As I work on developing a workflow with 4 steps in my web application, I encounter an issue when navigating from step 1 to step 4 and then back to step 1 without reloading the application. Upon repeating the path from step 1 to step 4 for the second time, ...

What impact do passing children have on the occurrence of Typescript errors?

Recently, I came across a peculiar situation where the Typescript compiler appeared to be confused by passing the children prop to a component, leading to unsafe behavior. I am looking to create a component that can only accept the subtitle (text) and sub ...

Tips for simulating behavior in express using Typescript and the Mocha library

Help with mocking 'Request' in Mocha using express with Typescript needed! Here is the current approach: describe("Authorization middleware", () => { it("Fails when no authorization header", () => { const req = { ...

Enhance the Nuxt 3 experience by expanding the functionality of the NuxtApp type with

When I include a plugin in my NuxtApp, it correctly identifies its type. https://i.stack.imgur.com/UFqZW.png However, when I attempt to use the plugin on a page, it only displays the type as "any." https://i.stack.imgur.com/hVSzA.png Is there a way for ...

obtain data from an array using Angular 5

i need assistance with retrieving values from an array. Below is my code snippet: this.RoleServiceService.getRoleById(this.id).subscribe(data => { this.roleData.push(data['data']); console.log(this.roleData); }) however, the resulting ar ...

Choosing between running a single unit test or multiple tests using Karma toggle switch

Currently, I am working on writing unit tests using the Karma/Jasmine combination and I'm curious if there's a way to target a specific spec file for testing instead of running all the spec files in the files array defined in the karma.conf.js fi ...

Angular8 Chart.js customizes the Y axis tick labels

Is there a way to dynamically adjust the Y-axis ticks in a chart.js graph? I attempted to modify them using the following commands: this.chartOptions.scales.yAxes[0].ticks.min = 10; this.chartOptions.scales.yAxes[0].ticks.max = 18; ...

Obtain a filtering dropdown list directly from the database within Ag-grid

Currently in my interface, I am attempting to implement a filter for the FOLDER column. This filter is supposed to retrieve data from the database and present it in a dropdown checkbox within that column. The filtering should be based on the selected data. ...

An exploration of navigating through a generic interface in Angular

I've recently started exploring Angular and I'm trying to incorporate a generic interface to reuse pagination models from different sources. Let me share some code examples to clarify. export interface IUser{ id: string; name: string; email: stri ...

Issue encountered while attempting to enclose a function within another function in Typescript

I'm attempting to wrap a function within another function before passing it to a library for later execution. However, I'm encountering various Typescript errors while trying to utilize .apply() and spread arguments. The library mandates that I ...

Head to the "/unauthorised" route in Angular while maintaining the current URL intact

I have a security guard that directs the user to an "/unauthorised" route when they do not have permission to access the current page (which could be any page). @Injectable() export class PermissionGuard implements CanActivate { constructor(private reado ...

Retrieving the status of checkboxes when dealing with a dynamic list in Angular2

I've been puzzling over this all day. I'm currently developing an application using Angular2, where I have a list of questions with a checkbox attached to each one. The list is dynamically incremented, so when a new question is added, it displays ...