Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
usarskyy opened this issue Oct 10, 2023 · 2 comments
Open

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

usarskyy opened this issue Oct 10, 2023 · 2 comments

Comments

@usarskyy
Copy link

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.

@ngbot ngbot bot added this to the needsTriage milestone Oct 11, 2023
@Kallepan
Copy link

Hi,
the easiest solution would probably be to update each control of your formgroup individually and only emit values on the desired fields. Like so:

          this.form.controls.countryCode2.setValue(data.countryCode2, { emitEvent: false });
          this.form.controls.itemPrice.setValue(data.itemPrice);
          this.form.controls.numberOfItems.setValue(data.numberOfItems);
          this.form.controls.vat.setValue(data.vat);

To prevent multiple firings of the combineLatest operator. You could use debounceTime and distinctUntilChanged.

anliben added a commit to po-ui/po-angular that referenced this issue Mar 4, 2024
Permite emissão de evento com `(p-change-model)` após resetar formulário reativo

Modificação feita por conta de um bug que impedia a emissão do evento após resetar o formulário reativo no Angular.

angular/angular#54022
angular/angular#52135
angular/angular#50971
angular/angular#46458
angular/angular#15741

Fixes DTHFUI-7232
pedrodominguesp pushed a commit to po-ui/po-angular that referenced this issue Mar 4, 2024
Permite emissão de evento com `(p-change-model)` após resetar formulário reativo

Modificação feita por conta de um bug que impedia a emissão do evento após resetar o formulário reativo no Angular.

angular/angular#54022
angular/angular#52135
angular/angular#50971
angular/angular#46458
angular/angular#15741

Fixes DTHFUI-7232
pedrodominguesp pushed a commit to po-ui/po-angular that referenced this issue Mar 4, 2024
Permite emissão de evento com `(p-change-model)` após resetar formulário reativo

Modificação feita por conta de um bug que impedia a emissão do evento após resetar o formulário reativo no Angular.

angular/angular#54022
angular/angular#52135
angular/angular#50971
angular/angular#46458
angular/angular#15741

Fixes DTHFUI-7232
@JeanMeche
Copy link
Member

Like @Kallepan I believe that this issue would solved by setting the values individually and prevent events when you don't want to trigger any additional updates.

Additing a context makes your logic dependent on side effects which are harder to reason on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants