Angular Reactive Forms valueChanges - UI changes only
Asked Answered
S

5

19

The Angular FormControl has a valueChanges Observable that states:

Emits an event every time the value of the control changes, in the UI or programmatically.

Is there a way to set the FormControl to ignore programmatic value changes? (Basically the equivalent of OneWayToSource binding in .NET)

Specifically, the issue I'm facing is that my valueChanges subscription, I'm updating a bunch the values bound to a bunch of other controls, which then causes valueChanges to fire for all of them as well, which is problematic as the actions they perform in their valueChanges handlers conflicts with the control the user actually touched.

Strikebreaker answered 16/4, 2018 at 15:43 Comment(0)
L
15

You can skip emitting the valueChange event by passing the option { emitEvent: false } to the setValue call.

setValue(value: any, options: {
    onlySelf?: boolean;
    emitEvent?: boolean;
    emitModelToViewChange?: boolean;
    emitViewToModelChange?: boolean;
} = {}): void

Also you might want to take a look at other options.

If onlySelf is true, this change will only affect the validation of this FormControl and not its parent component. This defaults to false.

If emitEvent is true, this change will cause a valueChanges event on the FormControl to be emitted. This defaults to true (as it falls through to updateValueAndValidity).

If emitModelToViewChange is true, the view will be notified about the new value via an onChange event. This is the default behavior if emitModelToViewChange is not specified.

If emitViewToModelChange is true, an ngModelChange event will be fired to update the model. This is the default behavior if emitViewToModelChange is not specified.

Docs

Listed answered 16/4, 2018 at 15:54 Comment(2)
Thanks, this is helpful. However, in my particular case, I'm update the values bound to the other form controls, not the form controls themselves. So I don't have the setValue method available, and it seems Angular's change detection is picking up on the changes and firing the events.Strikebreaker
How to resolve the issue with two interdependent controls having valueChange? I have a parasite 3rd event triggering (for initiator)Bonbon
M
2

You can pass { emitEvent: false } as options for the below reactive form methods to prevent them from triggering the valueChanges event

this.form.patchValue(value, { emitEvent: false })
this.form.setValue(value, { emitEvent: false })
this.form.controls.email.updateValueAndValidity({ emitEvent: false })
this.form.disable({ emitEvent: false })

yes disable triggers the valueChanges event

PS: above this.form is a reactive form

Read this excellent post, it'll answer all your questions and even give some great insights on reactive forms:

https://netbasal.com/angular-reactive-forms-tips-and-tricks-bb0c85400b58

Munt answered 15/5, 2020 at 8:28 Comment(0)
K
1

That may be helpful:

    yourControl.valueChanges.pipe(
        filter(() => yourControl.touched)
    ).subscribe(() => {
       ....
    })

It will fire only if control was previously touched. It doesnt fix all the issues, but may solve some cases.

Kurys answered 8/6, 2022 at 11:32 Comment(1)
While this is a smell, this actually wasn't bad as a "workaround" until we have the time to properly. When I say "smell" what I mean, is that the real method we had for determining whether or not value changes should propagate is not working. Why isn't it working... who knows!Jillane
M
0

The reactive form group have property yourFormName.pristine

You can use !yourFormName.pristine to detect only UI changes

 * A control is `pristine` if the user has not yet changed
 * the value in the UI.
 *
 * @returns True if the user has not yet changed the value in the UI; compare `dirty`.
 * Programmatic changes to a control's value do not mark it dirty.
 */
readonly pristine: boolean;
Mantellone answered 7/10, 2019 at 13:28 Comment(1)
pristine/dirty not reset after consequent patchValue's, are they?Chassis
T
-1

I could do it by putting this listener on the html file:

<ss-multiselect-dropdown ... (ngModelChange)="functionToTrigger($event)"></ss-multiselect-dropdown>

And on the .ts file:

functionToTrigger($event) {
    if (event === undefined) {
        // Put here are the instructions for UI changes
    } else {
        // Put here are the instructions for programmatic changes
}

I really don't know why it works this way. I came to this solution by trial and error.

Tourism answered 21/2, 2019 at 5:6 Comment(1)
The $event passed in the ngModelChange is just the value of the model. It has nothing to do with how the model was changed in the UI or not.Fugere

© 2022 - 2024 — McMap. All rights reserved.