40
votes

I have a formbuilder group and am listening for changes with valueChanges and triggering a save function followed by refresh function on the form:

 this.ticketForm.valueChanges.debounceTime(1000).distinctUntilChanged()
 .subscribe(data => {
   this.saveTicket();
   this.refreshTicket();
 })

I am then reloading the form and repatching the data to form fields (and elsewhere on the page, particularly a change log) with patchValue, e.g.:

    this.ticketForm.patchValue(ticket, { emitEvent: false });

however, this causes an infinite loop of saves of the form despite emitEvent : false.

Is this an Angular 4/Ionic 3 bug or a misunderstanding on my part?

6
That the patching of the value does not emit a change event that would be detected by valueChanges... - larpo
Same here, did you find answer for it? - Reza
With angular 5 It didn't fire the event with patchValue(value, {emitEvent: false}) - Amiram Korach
I'm using Angular 6 as well, and it's still firing with emitEvent: false... This isn't happening for everyone? - Methodician
I had the same issue, I realised that it was because I was using a customFormControl and my custom form control was not taking emitEvent: false into consideration on initialisation - Tonio

6 Answers

32
votes

Try adding onlySelf: true along with the emitEvent: false in this way:

this.ticketForm.patchValue(ticket, {emitEvent: false, onlySelf: true});
3
votes

Can't comment because of rep, so I will post it as an answer to @Craig Wayne.

emitEvent:false works only if you are listening to value changes on the form control with:

this.form.valueChanges.controlName.subscribe(val => doSomething(val));

if you are binding to model changes on the element event is emitted regardless:

<input (ngModelChange)="doSomething($event)"/>
2
votes

While working with Angular 9.1.13 I had been facing the same problem. Tried to use the FormControl.setValue, and FormGroup.patchValue APIs, using also the suggested params {emitEvent: false, onlySelf: true} in all possible combinations. The valueChanges observable is was still being triggered.

The only thing that worked for me eventually was :

myForm.disable();
myForm.patchValue({myControl: ''}, {onlySelf: true, emitEvent: false});
myForm.enable();
0
votes

As a workaround, i add skip to rxjs pipe

this.form.valueChanges
    .pipe(skip(1)).subscribe();
0
votes
this.filter_form.valueChanges
     .pipe(debounceTime(400), startWith(this.filter_form.value), pairwise(), takeUntil(this._OnDestroy))
     .subscribe(([prev, curr]) => {
       console.log(prev, curr)
       if(prev.date != curr.date && curr.date == 'select_date'){
         this.filter_form.patchValue({
            this.filter_form.get('from_date').setValue(moment().startOf('month').toDate(), {emitEvent: false});
         this.filter_form.get('to_date').setValue(moment().add('5','M').endOf('month').toDate(), {emitEvent: false});
         }, {emitEvent: false});
         this.getLeave();
       }
     });

i remove the debounceTime(400) and patch individual controls value using setValue

and it worked for me

0
votes

Why use a patchValue whenever a value changes instead of setting it once onInit?

Personally, I had this issue in a different context in building an option between two required FormControl's

I had a field clearing out another field (as designed, only one should be filled out), but then the subscribe triggered the original to clear. emitEvent:false didn't help because I needed my validators to run on an update. Adding if(value) helped it avoid cascade patching. See below:

this.formGroup.controls.item1.valueChanges.subscribe(value => {
   if(value){
     this.formGroup.patchValue({
       'item2':null
     })
   }
})

this.formGroup.controls.item2.valueChanges.subscribe(value => {
   if(value){
     this.formGroup.patchValue({
       'item1':null
     })
   }
})

Note: For simplicity sake, I didn't include the .pipe(takeUnitl(this.destroy$)). If you don't include this, it'll have a memory leak