Search code examples
angularionic-frameworkrxjsionic3angular-forms

Angular form change detection


I am using Angular forms, and I would like to use their built-in change detection to implement a functionality in my application. When a user clicks a button, he should only see a dialog if he/she has made any changes in the form.

I have changesMade variable:

private changesMade: boolean;

This is my TS code of the form:

this.carForm = new FormGroup({
        name: new FormControl('', [
            Validators.required,
            Validators.pattern('[a-zA-Z ]*'),
            Validators.minLength(1),
            Validators.maxLength(10)
        ])});

This is my HTML code of the form:

<form [formGroup]="carForm">
        <ion-item>
          <ion-label  stacked>car name</ion-label>
          <ion-input type="text" [(ngModel)]="carname" formControlName="name"></ion-input>
        </ion-item>
</form>

Here is my simulated(for now) service call where I subscribe to form changes after I've set the value of carname, which is bound to the input

setTimeout(()=>{
  this.carname = "BMW";
  this.carForm.valueChanges.subscribe(val => {
  this.changesMade = true;
    });
}, 44)

The problem here is that even though I haven't touched the form, this.changesMade gets set to true.

NOTE: If I move the subscribe part of the code in the ngAfterViewInit, it still sets the changesMade to true even though I haven't toushed the input:

  ngOnInit(){
    //simulated server call
    setTimeout(()=>{
      this.carname = "BMW";

    }, 44)
  }
      ngAfterViewInit(){
this.carForm.valueChanges.subscribe(val => {
      this.changesMade = true;
        });
    }

I have created a STACKBLITZ demonstrating the problem. How can I make it execute this.changesMade = true; only when I've actually physically touched the input in the UI?


Solution

  • So by putting the subscription at the end of the callstack (with setTimeout(0)) everything seems to be working as expected:

    //simulated server call
    setTimeout(()=>{
      this.carname = "BMW";
      setTimeout(function(){
      this.carForm.valueChanges.subscribe(val => {
      this.changesMade = true;
        });
      }.bind(this), 0)
    
    }, 44)
    

    Here is a STACKBLITZ demonstrating that it works.

    UPDATE

    Since using reactive forms with ngModel is deprecated in Angular 6+, it's better to replace the reactive forms with template driven forms in similar use cases.