fix(forms): number input fires valueChanges twice. (#36087)

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 #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 #12540

PR Close #36087
This commit is contained in:
Tidi 2020-03-16 15:24:53 +00:00 committed by Alex Rickabaugh
parent f7815cf96d
commit 97d6d909e7
2 changed files with 18 additions and 5 deletions

View File

@ -43,11 +43,7 @@ export const NUMBER_VALUE_ACCESSOR: any = {
@Directive({
selector:
'input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]',
host: {
'(change)': 'onChange($event.target.value)',
'(input)': 'onChange($event.target.value)',
'(blur)': 'onTouched()'
},
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
providers: [NUMBER_VALUE_ACCESSOR]
})
export class NumberValueAccessor implements ControlValueAccessor {

View File

@ -149,6 +149,23 @@ import {dispatchEvent} from '@angular/platform-browser/testing/src/browser_util'
expect(control.value).toEqual(0);
});
it('should ignore the change event', () => {
const fixture = initTest(FormControlNumberInput);
const control = new FormControl();
fixture.componentInstance.control = control;
fixture.detectChanges();
control.valueChanges.subscribe({
next: (value) => {
throw 'Input[number] should not react to change event';
}
});
const input = fixture.debugElement.query(By.css('input'));
input.nativeElement.value = '5';
dispatchEvent(input.nativeElement, 'change');
});
it('when value is cleared programmatically', () => {
const fixture = initTest(FormControlNumberInput);
const control = new FormControl(10);