18

I have some form and button to save it. The button must only be enabled when there are unsaved changes (inputs) on the form.

<form>
    <div>
    ... (inputs)
        <span (click)="save()"> Save </span>
    </div>
</form>

Is there some build-in mechanism for form dirty check in Angular 5? What is the easiest way to implement this scenario ?

Michael Dimmitt
  • 826
  • 10
  • 23
AllmanTool
  • 1,384
  • 1
  • 16
  • 26
  • You could use angulars form control. More infos are in the [docs](https://angular.io/guide/forms#track-control-state-and-validity-with-ngmodel) – Fussel May 18 '18 at 06:53

2 Answers2

24

Yes there is: I highly advise you to take a look at the documentation of reactive forms.

Apart from that, the built-in mechanism is only for checking the state of the form:

  • touched means the user has entered the form
  • dirty / !pristine means the user has made a modification

But if you want to handle changes made, you should not use that: if your username changes its username from "foo", to "bar", then back to "foo", there is no changes in your form, so the user should not have to send the said form.

Instead, what I advise you is to make a function that compares the form to the original value of your object. Here is how you can do it:

// Creates a reference of your initial value
createReference(obj: any) {
  this.reference = Object.assign({}, obj);
}

// Returns true if the user has changed the value in the form
isDifferent(obj: any, prop: string) {
  return this.reference[prop] !== obj[prop];
}

submitForm(form: any) {
  // ... Business code ...
  hasChanges = false;
  for (let prop in form) {
    if (this.isDifferent(form, prop)) { hasChanges = true; }
  }
  // If no changes, cancel form submition
  if (!hasChanges) { return; }
}
Joe Eifert
  • 1,306
  • 14
  • 29
  • 2
    This solution can be applied during ngOnChanges() instead of the submit click. Reactive Forms include a valueChanges() method that returns an observable as described here: https://alligator.io/angular/reactive-forms-valuechanges/ . Article was written in 2017. – Michael Dimmitt May 08 '20 at 15:23
13

When you are working with reactive forms (https://angular.io/guide/reactive-forms), there is a property pristine and a property dirty on the form-group and the control. Should look similar to this:

<form form-group="myGroup">
  <div>
    <input type="text" formControlName="ctrl1">
    ... (further inputs)

    <span><button (click)="save()" [disabled]="myGroup.pristine"> Save </button></span>
  </div>
</form>

and the .ts file:

import { Component, .... } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';

@Component({...})
export class YourFancyComponent {
  myGroup: FormGroup;
  constructor(private( formBuilder: FormBuilder) {
      this.myGroup = this.formBuilder.group({
        'ctrl1': 'defValue',
        'ctrl2': 'defaultValue2'
      });
  }
}

For template-driven forms (according to https://angular.io/guide/forms#track-control-state-and-validity-with-ngmodel) the css class of the modified input control changes from ng-pristine to ng-dirty but that doesn't help with the save button.

Chris
  • 1,508
  • 1
  • 11
  • 30