angular: Reactive Forms: modifying a FormArray does not flip dirty switch

I’m submitting a … (check one with “x”)

[x] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Current behavior When you add or remove FormGroup items to or from a FormArray, it does not cause the form to become dirty.

Expected behavior The root FormGroup of a form should be in the dirty state after modifying any descendant FormArray collection (by means of push/pop/removeAt/…).

Minimal reproduction of the problem with instructions

  1. Open the Reactive Forms Demo: https://angular.io/generated/live-examples/reactive-forms/final.eplnkr
  2. Select a hero
  3. Click ‘Add a Secret Lair’ one or more times.
  4. Scroll down to ensure that new secret lairs have been created in the form model. There should be at least one empty address at the end of the secretLairs array. Example:
heroForm value: {
  "name":"Whirlwind",
  "power":null,
  "sidekick":null,
  "secretLairs":[
    {
      "street":"123 Main",
      "city":"Anywhere",
      "state":"CA",
      "zip":"94801"
    },
    {
      "street":"456 Maple",
      "city":"Somewhere",
      "state":"VA",
      "zip":"23226"
    },
    {
      "street":"",
      "city":"",
      "state":"",
      "zip":""
    }
  ]
}
  1. Scroll up and observe that the ‘Save’ and ‘Revert’ buttons are still disabled. Expected result: the buttons should be enabled.

What is the motivation / use case for changing the behavior?

Modifying a FormArray collection causes the form model to change so the form should not be considered pristine.

Angular version: 4.0.3

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 13
  • Comments: 19 (6 by maintainers)

Most upvoted comments

As @rafaelss95 mentioned, this is expected behavior. The “dirty” property on a form control was designed to indicate whether a user has interacted with the form. If programmatic changes were also to make a control dirty, this would defeat the purpose of the property because you’d no longer be able to tell when a value change came from the UI or from application code.

In the case of a button click that then calls some form method - the forms API has no way of knowing whether this ultimately comes from a user action or whether you’re simply setting up the data of the form. You can always call markAsDirty() in these cases. Closing, as this works as expected.

@kara While most of the discussion is focusing around programmatic changes to the form, the dirty property is still not being changed on manual user input on nested FormContorls. Currently i have the following case:

form: FormGroup = new FormGroup(
arr: new FormArray([])
)

Programmatically a undefined set of FormGroups are pushed onto the FormArray

arr: [
FormGroup1: {...},
FormGroup2: {...},
FormGroup3: {...},
...
]

Manual user input will change the dirty property of FormGroupN (1,2…) to true but will not pass it to its parents. Is this expected behaviour? It certainly results in way more complex logic than it has to be. Please consider reopening the issue.

When the FormArray is updated via the interface, the control in the FormArray is marked dirty yet the FormGroup remains pristine.

@cbaltrinic We recently overcame a similar issue, which was to do with directly assigning the FormArray. For example:

// note: the FormGroup containing an empty FormArray was set up earlier

// the following code would result in a FormArray which correctly marks itself and child controls
//   as dirty/touched/valid etc, while the parent FormGroup is oblivious to these state changes

(this.form.controls.contactInfo as FormGroup).controls.phones = new FormArray(
  phonesFromServer.map((phone: Phone): FormGroup => {
    const formGroup = this.emptyPhoneNumberForm.formGroup;
    formGroup.patchValue(phone);
    return formGroup;
  })
);

With a small modification, this behavior is no longer exhibited:

// note: the FormGroup containing an empty FormArray was set up earlier

// this... "just works" (tm)

phonesFromServer.forEach((phone: Phone): void => {

  const formGroup = this.emptyPhoneNumberForm.formGroup;
  formGroup.patchValue(phone);

  ((this.form.controls.contactInfo as FormGroup).controls.phones as FormArray).push(formGroup);
});

I am having the same issue. I have a form group with one of the controls being a FormArray. When the FormArray is updated via the interface, the control in the FormArray is marked dirty yet the FormGroup remains pristine.

As mentioned earlier, the problem extends to not recognizing user interaction. For example, I’m dynamically creating a forms array with input fields, but populating those input fields in the browser doesn’t affect the pristine status…i would hope that would be considered a valid bug. FWIW, I agree with the decision to close as expected for programmatic changes.

can confirm that the formarray is dirty but he parent group is infact pristine … can i work around ya… is this correct… i would say no

workaround this.form.value.volunteerReferences.dirty

image