Skip to content

Partial ignore of ValueChanges event when Reactive Form value is set #52135

@usarskyy

Description

@usarskyy

Which @angular/* package(s) are relevant/related to the feature request?

forms

Description

Demo app: https://github.com/usarskyy/form-initialization

I understand that below described use case is not really related to Angular Forms because your package offers a basic foundation, not the solution to all kind of problems in the world. But maybe you could consider my comments when implementing other features that eventually can help me as well. Or since you communicate with the community a lot, you could point me to the solution that was already implemented (BTW, I looked for already existing external library but couldn't find any).

Use case

Reactive forms offer two methods to update its value: setValue and patchValue. Options for both methods include emitEvent parameter that prevents valueChanges from being triggered when set to true.

Quite often I see forms that include auto-populated inputs based on previously selected values (for example, a person selects a country from dropdown and a VAT-input is set automatically). These inputs are usually overridable by a user. For example, if a user selects Germany from dropdown and a VAT-input is set to 19%, the user can still override this value to any other value.

Also these forms include read-only fields that are calculated based on other fields (for example, total value = number of items * single item price). These fields cannot be changed by a user directly.

When a user starts populating this kind of forms, everything works as expected: s/he goes step by step until the end, all fields get pre-populated and pre-calculated upfront correctly. But as soon as I need to save a draft version of this form on the server and then restore it, I see that auto-populated fields with overridden values are overwritten (this behavior you can see in my demo app if you navigate to "Problem existing" page; see how VAT value changes after 300ms). It happens because valueChanges triggers request to the server, which overrides correct input value with whatever the default value from the server.
The obvious solution is to pass { emitEvent: true } to disable valueChanges event, so no request to the server is sent. Unfortunately this will also break calculated read-only fields.

How to fix this issue?

There are a few ways how we could make our app work correctly:

  1. check if an auto-populated input has a value before overwriting it. Easy to implement and from my experience, this will do the job in most of the cases. But it won't work, for example, for inputs where an empty or null value is also considered correct -> then you don't know if input's value is empty because it was not yet updated or it is expected to be empty/null.
  2. unsubscribe all handlers that override auto-populated inputs before we update form's value, then subscribe again. Works in 100% cases.
    In my demo app this approach can be found on "Simple existing" page.
  3. block some notifications from valueChanges event when form value is updated. Works in 100% cases.
    In my demo app this approach can be found on "InitVar existing" and "Extended FormGroup existing" page. The code is super straightforward:
this.form.controls.countryCode2.valueChanges
        .pipe(
          skipWhile(() => {
            return this.#initializing;
          }),
...

Personally I prefer the third option because it is not so messy, requires less code to implement and is easy to read.
This approach can be implemented either by using directly an external component's variable or by creating an extended version of a FormGroup class.

When blocking valueChanges won't work?

Currently my preferred solution works because RxJS executes all valueChanges subscriptions immediately (basically, you could say "synchronously"), so the value of #initializing variable is always correct. But this behavior is not guaranteed and if valueChanges subscriptions are executed later because, for example, scheduler is changed, then there will be no way to know that a notification must be ignored.

Proposed solution

Introduce some kind of context as an additional parameter when setValue or patchValue method is called. Then this context can be used to ignore the valueChanges event.

Alternatives considered

Custom implementations described above.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions