feat(input): ionChange will only emit from user committed changes (#25858)

Resolves #20106, #20061
This commit is contained in:
Sean Perkins
2022-09-12 15:35:43 -04:00
committed by GitHub
parent ba6b539675
commit 8732b7bdb7
18 changed files with 225 additions and 30 deletions

View File

@ -25,6 +25,6 @@ export class BooleanValueAccessorDirective extends ValueAccessor {
@HostListener('ionChange', ['$event.target'])
_handleIonChange(el: any): void {
this.handleChangeEvent(el, el.checked);
this.handleValueChange(el, el.checked);
}
}

View File

@ -0,0 +1,5 @@
export * from './boolean-value-accessor';
export * from './numeric-value-accessor';
export * from './radio-value-accessor';
export * from './select-value-accessor';
export * from './text-value-accessor';

View File

@ -18,13 +18,13 @@ export class NumericValueAccessorDirective extends ValueAccessor {
super(injector, el);
}
@HostListener('ionChange', ['$event.target'])
_handleIonChange(el: any): void {
this.handleChangeEvent(el, el.value);
@HostListener('ionInput', ['$event.target'])
handleInputEvent(el: HTMLIonInputElement): void {
this.handleValueChange(el, el.value);
}
registerOnChange(fn: (_: number | null) => void): void {
super.registerOnChange((value) => {
super.registerOnChange((value: string) => {
fn(value === '' ? null : parseFloat(value));
});
}

View File

@ -21,6 +21,6 @@ export class RadioValueAccessorDirective extends ValueAccessor {
@HostListener('ionSelect', ['$event.target'])
_handleIonSelect(el: any): void {
this.handleChangeEvent(el, el.checked);
this.handleValueChange(el, el.checked);
}
}

View File

@ -21,6 +21,6 @@ export class SelectValueAccessorDirective extends ValueAccessor {
@HostListener('ionChange', ['$event.target'])
_handleChangeEvent(el: any): void {
this.handleChangeEvent(el, el.value);
this.handleValueChange(el, el.value);
}
}

View File

@ -5,7 +5,7 @@ import { ValueAccessor } from './value-accessor';
@Directive({
/* tslint:disable-next-line:directive-selector */
selector: 'ion-input:not([type=number]),ion-textarea,ion-searchbar',
selector: 'ion-textarea,ion-searchbar',
providers: [
{
provide: NG_VALUE_ACCESSOR,
@ -21,6 +21,27 @@ export class TextValueAccessorDirective extends ValueAccessor {
@HostListener('ionChange', ['$event.target'])
_handleInputEvent(el: any): void {
this.handleChangeEvent(el, el.value);
this.handleValueChange(el, el.value);
}
}
@Directive({
selector: 'ion-input:not([type=number])',
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: InputValueAccessorDirective,
multi: true,
},
],
})
export class InputValueAccessorDirective extends ValueAccessor {
constructor(injector: Injector, el: ElementRef) {
super(injector, el);
}
@HostListener('ionInput', ['$event.target'])
_handleInputEvent(el: any): void {
this.handleValueChange(el, el.value);
}
}

View File

@ -29,7 +29,20 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
setIonicClasses(this.el);
}
handleChangeEvent(el: HTMLElement, value: any): void {
/**
* Notifies the ControlValueAccessor of a change in the value of the control.
*
* This is called by each of the ValueAccessor directives when we want to update
* the status and validity of the form control. For example with text components this
* is called when the ionInput event is fired. For select components this is called
* when the ionChange event is fired.
*
* This also updates the Ionic form status classes on the element.
*
* @param el The component element.
* @param value The new value of the control.
*/
handleValueChange(el: HTMLElement, value: any): void {
if (el === this.el.nativeElement) {
if (value !== this.lastValue) {
this.lastValue = value;

View File

@ -3,7 +3,10 @@ export { BooleanValueAccessorDirective as BooleanValueAccessor } from './directi
export { NumericValueAccessorDirective as NumericValueAccessor } from './directives/control-value-accessors/numeric-value-accessor';
export { RadioValueAccessorDirective as RadioValueAccessor } from './directives/control-value-accessors/radio-value-accessor';
export { SelectValueAccessorDirective as SelectValueAccessor } from './directives/control-value-accessors/select-value-accessor';
export { TextValueAccessorDirective as TextValueAccessor } from './directives/control-value-accessors/text-value-accessor';
export {
TextValueAccessorDirective as TextValueAccessor,
InputValueAccessorDirective as InputValueAccessor,
} from './directives/control-value-accessors/text-value-accessor';
export { IonTabs } from './directives/navigation/ion-tabs';
export { IonBackButtonDelegateDirective as IonBackButtonDelegate } from './directives/navigation/ion-back-button';
export { NavDelegate } from './directives/navigation/nav-delegate';

View File

@ -3,11 +3,14 @@ import { ModuleWithProviders, APP_INITIALIZER, NgModule, NgZone } from '@angular
import { IonicConfig } from '@ionic/core';
import { appInitialize } from './app-initialize';
import { BooleanValueAccessorDirective } from './directives/control-value-accessors/boolean-value-accessor';
import { NumericValueAccessorDirective } from './directives/control-value-accessors/numeric-value-accessor';
import { RadioValueAccessorDirective } from './directives/control-value-accessors/radio-value-accessor';
import { SelectValueAccessorDirective } from './directives/control-value-accessors/select-value-accessor';
import { TextValueAccessorDirective } from './directives/control-value-accessors/text-value-accessor';
import {
BooleanValueAccessorDirective,
NumericValueAccessorDirective,
RadioValueAccessorDirective,
SelectValueAccessorDirective,
TextValueAccessorDirective,
InputValueAccessorDirective,
} from './directives/control-value-accessors';
import { IonBackButtonDelegateDirective } from './directives/navigation/ion-back-button';
import { IonRouterOutlet } from './directives/navigation/ion-router-outlet';
import { IonTabs } from './directives/navigation/ion-tabs';
@ -38,6 +41,7 @@ const DECLARATIONS = [
RadioValueAccessorDirective,
SelectValueAccessorDirective,
TextValueAccessorDirective,
InputValueAccessorDirective,
// navigation
IonTabs,