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

Number input fires valueChanges twice #12540

Closed
manofearth opened this issue Oct 26, 2016 · 59 comments
Closed

Number input fires valueChanges twice #12540

manofearth opened this issue Oct 26, 2016 · 59 comments

Comments

@manofearth
Copy link

@manofearth manofearth commented Oct 26, 2016

May be bug.

ReactiveFormsModule. Input with type "number" fires valueChanges event twice when changed from browser.

I expect it to fire valueChanges once.

Here is a plunkr example: https://plnkr.co/edit/HYCtlP72W3edsaZe9HtR?p=preview

I use ngrx/store to manage state and subscribe my store to valueCahnges observable. When I change value of input, reducers fires twice. It is not optimal, I suppose.

Angular version: 2.1.0

Browser: Chrome

@robwormald
Copy link
Contributor

@robwormald robwormald commented Oct 26, 2016

Hmm, yeah, looks like a bug to me. We're listening to both events https://github.com/angular/angular/blob/master/modules/%40angular/forms/src/directives/number_value_accessor.ts#L34-L35 - depending on the browser, that'll fire twice in certain scenarios.

cc @kara

@buddyackerman
Copy link

@buddyackerman buddyackerman commented Dec 20, 2016

Curious if this is going to be fixed soon.

@buddyackerman
Copy link

@buddyackerman buddyackerman commented Feb 2, 2017

This has been open for quite a while, is it going to be fixed soon?

@DzmitryShylovich
Copy link
Contributor

@DzmitryShylovich DzmitryShylovich commented Feb 2, 2017

@buddyackerman browser?

@buddyackerman
Copy link

@buddyackerman buddyackerman commented Feb 2, 2017

@DzmitryShylovich Chrome, FF, IE11

@DzmitryShylovich
Copy link
Contributor

@DzmitryShylovich DzmitryShylovich commented Feb 2, 2017

We will be able to fix it when #12990 will be merged

@buddyackerman
Copy link

@buddyackerman buddyackerman commented Feb 2, 2017

@DzmitryShylovich OK, any idea when that will be?

@DzmitryShylovich
Copy link
Contributor

@DzmitryShylovich DzmitryShylovich commented Feb 2, 2017

no idea :)

@robachma
Copy link

@robachma robachma commented Feb 26, 2017

Wondering when this will be fixed. I think it is causing the same issue that I am facing.

setValue() { this.myForm.controls['control1'].valueChanges.subscribe({ next: (change) => { if (change = 'Yes') { this.myForm.controls['controlGroup'].enable() } else if (change = 'No') { this.myForm.controls['controlGroup'].disable(); } } });

Using a radio button for this. When the value changes to 'Yes', it is working as expected. But when 'No' is selected, it does not disable my control group.

Not positive if these are related. Please help. Thx

@makiJS
Copy link

@makiJS makiJS commented Jun 8, 2017

+1
version "@angular/forms": "2.0.0"

@ebem
Copy link

@ebem ebem commented Jun 12, 2017

+1 "@angular/forms": "^4.0.0"

@ChrisDevinePimss
Copy link

@ChrisDevinePimss ChrisDevinePimss commented Jul 26, 2017

am still getting this issue in latest angular, any idea on a fix date?

@sebinbenjamin
Copy link

@sebinbenjamin sebinbenjamin commented Aug 15, 2017

same issue in "@angular/forms": "4.2.3",

@michellequack
Copy link

@michellequack michellequack commented Oct 12, 2017

This is actually a huge issue for us. We send significant data back to the server on some changes (customer requirement for autosave) and it is causing our application to be just too slow to use angular reactive forms. Is there any chance at all that this will be fixed soon?

@dgroh
Copy link

@dgroh dgroh commented Nov 10, 2017

+1

@tomas-cani
Copy link

@tomas-cani tomas-cani commented Nov 24, 2017

Same problem as you @michellequack. As a temporary workaround you may want to use distinctUntilChanged

@manichandra
Copy link

@manichandra manichandra commented Dec 26, 2017

Thanks @tomas-cani . It works perfectly. Used Observable.fromEvent() to capture the (onchange) event. I am using it for file upload feature.

@carlospalacin
Copy link

@carlospalacin carlospalacin commented Jan 8, 2018

same issue in "@angular/forms": "5.1.3"

@magoss

This comment has been minimized.

@joshuaballas
Copy link

@joshuaballas joshuaballas commented Mar 15, 2018

Having this same issue on v5.2.9. Causing multiple API hits on component load.

@Provson
Copy link

@Provson Provson commented Apr 17, 2018

Same issue on v5.2.10, any idea ?

@dimiork
Copy link

@dimiork dimiork commented Jan 9, 2020

Temporary solution: RXJS debounceTime operator: https://www.learnrxjs.io/operators/filtering/debouncetime.html`

@SLGShark6
Copy link

@SLGShark6 SLGShark6 commented Jan 9, 2020

https://gist.github.com/SLGShark6/806b5f756db48dbe69da24fbaa92e23d

Edit:
If you include this in the module where you import the angular forms module, it SHOULD replace the default NumberValueAccessor, in case that was not clear.

Tested Angular 8+.

@traviskaufman
Copy link

@traviskaufman traviskaufman commented Jan 10, 2020

Just an FYI that this basically makes number fields impossible to use with async validators 😄. @kara @robwormald are you open to contributions for a fix? This really seems like something that if fixed could really improve DX and lead to less surprises.

@deumax
Copy link

@deumax deumax commented Jan 13, 2020

I experienced a similiar issue with text input fields, but it was a different problem.
I have a custom component which emits an event if form values change. The problem was that I named the output event 'change', which was fired a second time (presumedly on blur). Renaming 'change' to 'filterChange' fixed my problem.

@traviskaufman
Copy link

@traviskaufman traviskaufman commented Jan 18, 2020

FWIW I was able to fix the number fields issue I mentioned above by creating a custom directive extending NumberValueAccessor. This class stores any previous values emitted by the host element, and implements @HostListener('input') and @HostListener('change') which only call onChange() if the given value is different than the stored value. Might not be perfect for every use-case but worked for mine!

import {Directive, HostListener, forwardRef} from '@angular/core';
import {NumberValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';

export const FIXED_NUMBER_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => FixedNumberValueAccessor),
  multi: true
};

@Directive({
  selector: 'input[type=number][formControl][fixedNumberField],input[type=number][formControlName][fixedNumberField],input[type=number][ngModel][fixedNumberField]',
  providers: [FIXED_NUMBER_VALUE_ACCESSOR],
})
export class FixedNumberValueAccessor extends NumberValueAccessor {
  private lastValue: string;
  
  @HostListener('change', ['$event.target.value']) handleOnChange(value) {
    this.handleUpdate(value);
  }

  @HostListener('input', ['$event.target.value']) handleOnInput(value) {
     this.handleUpdate(value);
  }

  private handleUpdate(value: string) {
    if (value === this.lastValue) {
      return;
    }
    this.lastValue = value;
    this.onChange(value);
  }
}

StackBlitz

Would love to see a longer-term fixed prioritized and merged into core 😄

@SLGShark6
Copy link

@SLGShark6 SLGShark6 commented Jan 19, 2020

FWIW I was able to fix the number fields issue I mentioned above by creating a custom directive extending NumberValueAccessor. This class stores any previous values emitted by the host element, and implements @HostListener('input') and @HostListener('change') which only call onChange() if the given value is different than the stored value. Might not be perfect for every use-case but worked for mine!

import {Directive, HostListener, forwardRef} from '@angular/core';
import {NumberValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';

export const FIXED_NUMBER_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => FixedNumberValueAccessor),
  multi: true
};

@Directive({
  selector: 'input[type=number][formControl][fixedNumberField],input[type=number][formControlName][fixedNumberField],input[type=number][ngModel][fixedNumberField]',
  providers: [FIXED_NUMBER_VALUE_ACCESSOR],
})
export class FixedNumberValueAccessor extends NumberValueAccessor {
  private lastValue: string;
  
  @HostListener('change', ['$event.target.value']) handleOnChange(value) {
    this.handleUpdate(value);
  }

  @HostListener('input', ['$event.target.value']) handleOnInput(value) {
     this.handleUpdate(value);
  }

  private handleUpdate(value: string) {
    if (value === this.lastValue) {
      return;
    }
    this.lastValue = value;
    this.onChange(value);
  }
}

StackBlitz

Would love to see a longer-term fixed prioritized and merged into core 😄

See my comment above, for just completely replacing it in the DI container,
https://gist.github.com/SLGShark6/806b5f756db48dbe69da24fbaa92e23d

@turulb
Copy link

@turulb turulb commented Jan 23, 2020

+1
Because of this issue, two requests are sent to my backend, however, I need only when the value is changed, I don't need the value change event on blur.

@marcodafonseca
Copy link

@marcodafonseca marcodafonseca commented Jan 24, 2020

+1
Discovered this bug recently. It affects my forms where calculations are done from a mobile device. Older devices get terribly slow to hide the keyboard due to events firing twice

@mrpharderwijk
Copy link

@mrpharderwijk mrpharderwijk commented Feb 1, 2020

+1
I got this on an input with [(ngModel)] & (ngModelChange), the ngModelChange function is called twice. If I change my setup for using formControls and subscribe to the valueChanges it fires twice too, so this doesn't make a difference...

@andreyinkin
Copy link

@andreyinkin andreyinkin commented Feb 4, 2020

+1
The issue occurs only when clicking up or down arrows on the input fiewld

@musk-coding

This comment has been minimized.

@laptrinhvien1994
Copy link

@laptrinhvien1994 laptrinhvien1994 commented Mar 13, 2020

+1
I got the same issue.
For now, I used RxJS operator to handle it.

TidianeRolland added a commit to TidianeRolland/angular that referenced this issue Mar 16, 2020
Prior to this commit, input field of type number used to fire valueChanges twice. First time after typing in the input field and second time when the input field loses the focus.

PR Close angular#12540
TidianeRolland added a commit to TidianeRolland/angular that referenced this issue Mar 17, 2020
Prior to this commit, input field of type number used to fire valueChanges twice. First time after typing in the input field and second time when the input field loses the focus.
The `input event` is enough for valueChanges Observable, and is supported by both modern and old browsers like IE9-11.

PR Close angular#12540
TidianeRolland added a commit to TidianeRolland/angular that referenced this issue Mar 17, 2020
Prior to this commit, input field of type number used to fire valueChanges twice. First time after typing in the input field and second time when the input field loses the focus.
The `input event` is enough for valueChanges Observable, and is supported by both modern and old browsers like IE9-11.

PR Close angular#12540
TidianeRolland added a commit to TidianeRolland/angular that referenced this issue May 2, 2020
Prior to this commit, input field of type number used to fire valueChanges twice. First time after typing in the input field and second time when the input field loses the focus. The `input event` is enough for valueChanges Observable.

BREAKING CHANGE: <description> (tests that could break because they rely on 'change' event, requirement for devs to add their own `(change)` listener if they want to support IE9 in the same way it is now, etc)
PR Close angular#12540
TidianeRolland added a commit to TidianeRolland/angular that referenced this issue May 2, 2020
Prior to this commit, input field of type number used to fire valueChanges twice. First time after typing in the input field and second time when the input field loses the focus. The `input event` is enough for valueChanges Observable.

BREAKING CHANGE: <description> (tests that could break because they rely on 'change' event, requirement for devs to add their own `(change)` listener if they want to support IE9 in the same way it is now, etc)
PR Close angular#12540
TidianeRolland added a commit to TidianeRolland/angular that referenced this issue May 2, 2020
Prior to this commit, input field of type number used to fire valueChanges twice.
First time after typing in the input field and second time when the input field loses the focus.
The `input event` is enough for valueChanges Observable.

BREAKING CHANGE: <description> (tests that could break because they rely on 'change' event, requirement for devs to add their own `(change)` listener if they want to support IE9 in the same way it is now, etc)
PR Close angular#12540
TidianeRolland added a commit to TidianeRolland/angular that referenced this issue May 4, 2020
Prior to this commit, input field of type number used to fire valueChanges twice.
First time after typing in the input field and second time when the input field loses the focus.
The `input event` is enough for valueChanges Observable.

BREAKING CHANGE: We no longer listen to the `change` event. So, All tests that rely on `change` event could break.
We should now listen to `input` event.
Additionally, this change breaks IE9, because input events do not fire with backspace or cut actions on IE9.
Devs who want to support IE9, will have to add their own `change` listener.
PR Close angular#12540
alxhub added a commit to TidianeRolland/angular that referenced this issue May 5, 2020
Prior to this commit, number input fields would to fire valueChanges twice: once for `input` events when typing and second for the `change` event when the field lost focus (both events happen at once when using the increment and decrement buttons on the number field).

Fixes angular#12540

BREAKING CHANGE: Number inputs no longer listen to the `change` event.
* Tests which trigger `change` events need to be updated to trigger `input` events instead.
* The `change` event was in place to support IE9, as we found that `input` events were not fired with backspace or cut actions. If you need to maintain IE9 support, you will need to add a change event listener to number inputs and call the `onChange` method of `NumberValueAccessor` manually.
* Lastly, old versions of WebDriver would synthetically trigger the `change` event on `WebElement.clear` and `WebElement.sendKeys`. If you are using an old version of WebDriver, you may need to update tests to ensure `input` events are triggered. For example, you could use `element.sendKeys(Keys.chord(Keys.CONTROL, "a"), Keys.BACK_SPACE);` in place of `element.clear()`.

PR Close angular#12540
alxhub added a commit to TidianeRolland/angular that referenced this issue May 5, 2020
Prior to this commit, number input fields would to fire valueChanges twice: once for `input` events when typing and second for the `change` event when the field lost focus (both events happen at once when using the increment and decrement buttons on the number field).

Fixes angular#12540

BREAKING CHANGE: Number inputs no longer listen to the `change` event.
* Tests which trigger `change` events need to be updated to trigger `input` events instead.
* The `change` event was in place to support IE9, as we found that `input` events were not fired with backspace or cut actions. If you need to maintain IE9 support, you will need to add a change event listener to number inputs and call the `onChange` method of `NumberValueAccessor` manually.
* Lastly, old versions of WebDriver would synthetically trigger the `change` event on `WebElement.clear` and `WebElement.sendKeys`. If you are using an old version of WebDriver, you may need to update tests to ensure `input` events are triggered. For example, you could use `element.sendKeys(Keys.chord(Keys.CONTROL, "a"), Keys.BACK_SPACE);` in place of `element.clear()`.

PR Close angular#12540
@alxhub alxhub closed this in 97d6d90 May 7, 2020
@maxdulieu
Copy link

@maxdulieu maxdulieu commented May 7, 2020

Right on!

@theCrius
Copy link

@theCrius theCrius commented May 8, 2020

OMG I can't believe it! I'll have to go back and remove all the workarounds now 🤣 👏

We needed a pandemic to have the time to fix this issue :D

@andreElrico
Copy link

@andreElrico andreElrico commented May 12, 2020

I was about to file an issue: https://stackblitz.com/edit/angular-ahq3v1?file=src/app/hello.component.ts

Wow, its been open for almost 4 years despite being a performance killer and easy to fix.

Thanks Alex for fixing <3

@Splaktar
Copy link
Member

@Splaktar Splaktar commented May 31, 2020

We needed a pandemic to have the time to fix this issue :D

That wasn't quite it. There was a blocker to fixing this that was finally resolved.

@angular-automatic-lock-bot
Copy link

@angular-automatic-lock-bot angular-automatic-lock-bot bot commented Jul 1, 2020

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Jul 1, 2020
profanis added a commit to profanis/angular that referenced this issue Sep 5, 2020
Prior to this commit, number input fields would to fire valueChanges twice: once for `input` events when typing and second for the `change` event when the field lost focus (both events happen at once when using the increment and decrement buttons on the number field).

Fixes angular#12540

BREAKING CHANGE: Number inputs no longer listen to the `change` event.
* Tests which trigger `change` events need to be updated to trigger `input` events instead.
* The `change` event was in place to support IE9, as we found that `input` events were not fired with backspace or cut actions. If you need to maintain IE9 support, you will need to add a change event listener to number inputs and call the `onChange` method of `NumberValueAccessor` manually.
* Lastly, old versions of WebDriver would synthetically trigger the `change` event on `WebElement.clear` and `WebElement.sendKeys`. If you are using an old version of WebDriver, you may need to update tests to ensure `input` events are triggered. For example, you could use `element.sendKeys(Keys.chord(Keys.CONTROL, "a"), Keys.BACK_SPACE);` in place of `element.clear()`.

PR Close angular#12540

PR Close angular#36087
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.