diff --git a/BREAKING.md b/BREAKING.md
index 9288951b47..3768cb46ec 100644
--- a/BREAKING.md
+++ b/BREAKING.md
@@ -14,6 +14,7 @@ This is a comprehensive list of the breaking changes introduced in the major ver
- [Browser and Platform Support](#version-7x-browser-platform-support)
- [Components](#version-7x-components)
+ - [Input](#version-7x-input)
- [Overlays](#version-7x-overlays)
- [Range](#version-7x-range)
- [Slides](#version-7x-slides)
@@ -50,6 +51,12 @@ This section details the desktop browser, JavaScript framework, and mobile platf
Components
+
+
+`ionChange` is no longer emitted when the `value` of `ion-input` is modified externally. `ionChange` is only emitted from user committed changes, such as typing in the input and the input losing focus or from clicking the clear action within the input.
+
+If your application requires immediate feedback based on the user typing actively in the input, consider migrating your event listeners to using `ionInput` instead.
+
Overlays
Ionic now listens on the `keydown` event instead of the `keyup` event when determining when to dismiss overlays via the "Escape" key. Any applications that were listening on `keyup` to suppress this behavior should listen on `keydown` instead.
diff --git a/angular/src/directives/control-value-accessors/boolean-value-accessor.ts b/angular/src/directives/control-value-accessors/boolean-value-accessor.ts
index ac15f5b6b6..b8484516e4 100644
--- a/angular/src/directives/control-value-accessors/boolean-value-accessor.ts
+++ b/angular/src/directives/control-value-accessors/boolean-value-accessor.ts
@@ -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);
}
}
diff --git a/angular/src/directives/control-value-accessors/index.ts b/angular/src/directives/control-value-accessors/index.ts
new file mode 100644
index 0000000000..f18d651097
--- /dev/null
+++ b/angular/src/directives/control-value-accessors/index.ts
@@ -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';
diff --git a/angular/src/directives/control-value-accessors/numeric-value-accessor.ts b/angular/src/directives/control-value-accessors/numeric-value-accessor.ts
index e7ce54f671..33401ee0e1 100644
--- a/angular/src/directives/control-value-accessors/numeric-value-accessor.ts
+++ b/angular/src/directives/control-value-accessors/numeric-value-accessor.ts
@@ -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));
});
}
diff --git a/angular/src/directives/control-value-accessors/radio-value-accessor.ts b/angular/src/directives/control-value-accessors/radio-value-accessor.ts
index 192f10b58c..a3781e9247 100644
--- a/angular/src/directives/control-value-accessors/radio-value-accessor.ts
+++ b/angular/src/directives/control-value-accessors/radio-value-accessor.ts
@@ -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);
}
}
diff --git a/angular/src/directives/control-value-accessors/select-value-accessor.ts b/angular/src/directives/control-value-accessors/select-value-accessor.ts
index 36c36f218a..d46c73ca43 100644
--- a/angular/src/directives/control-value-accessors/select-value-accessor.ts
+++ b/angular/src/directives/control-value-accessors/select-value-accessor.ts
@@ -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);
}
}
diff --git a/angular/src/directives/control-value-accessors/text-value-accessor.ts b/angular/src/directives/control-value-accessors/text-value-accessor.ts
index b59d475fcc..abcc581dea 100644
--- a/angular/src/directives/control-value-accessors/text-value-accessor.ts
+++ b/angular/src/directives/control-value-accessors/text-value-accessor.ts
@@ -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);
}
}
diff --git a/angular/src/directives/control-value-accessors/value-accessor.ts b/angular/src/directives/control-value-accessors/value-accessor.ts
index 615d736828..65ac7a4e7e 100644
--- a/angular/src/directives/control-value-accessors/value-accessor.ts
+++ b/angular/src/directives/control-value-accessors/value-accessor.ts
@@ -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;
diff --git a/angular/src/index.ts b/angular/src/index.ts
index 38ba6c1c72..833fe68411 100644
--- a/angular/src/index.ts
+++ b/angular/src/index.ts
@@ -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';
diff --git a/angular/src/ionic-module.ts b/angular/src/ionic-module.ts
index 34afe3f08e..f292b5ca1f 100644
--- a/angular/src/ionic-module.ts
+++ b/angular/src/ionic-module.ts
@@ -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,
diff --git a/angular/test/base/e2e/src/form.spec.ts b/angular/test/base/e2e/src/form.spec.ts
index 165ae68b10..37cad16281 100644
--- a/angular/test/base/e2e/src/form.spec.ts
+++ b/angular/test/base/e2e/src/form.spec.ts
@@ -36,7 +36,9 @@ describe('Form', () => {
});
it('should become valid', () => {
- cy.get('ion-input.required').invoke('prop', 'value', 'Some value');
+ cy.get('ion-input.required').type('Some value');
+ cy.get('ion-input.required input').blur();
+
testStatus('INVALID');
// TODO: FW-1160 - Remove when v7 is released
diff --git a/angular/test/base/e2e/src/inputs.spec.ts b/angular/test/base/e2e/src/inputs.spec.ts
index 9cc4f4450e..c04296c567 100644
--- a/angular/test/base/e2e/src/inputs.spec.ts
+++ b/angular/test/base/e2e/src/inputs.spec.ts
@@ -40,7 +40,10 @@ describe('Inputs', () => {
cy.get('ion-checkbox').invoke('prop', 'checked', true);
cy.get('ion-toggle').invoke('prop', 'checked', true);
- cy.get('ion-input').invoke('prop', 'value', 'hola');
+
+ cy.get('ion-input').eq(0).type('hola');
+ cy.get('ion-input input').eq(0).blur();
+
cy.get('ion-datetime').invoke('prop', 'value', '1996-03-15');
cy.get('ion-select').invoke('prop', 'value', 'playstation');
cy.get('ion-range').invoke('prop', 'value', 20);
diff --git a/core/src/components.d.ts b/core/src/components.d.ts
index ace6c43037..56bf4cb934 100644
--- a/core/src/components.d.ts
+++ b/core/src/components.d.ts
@@ -5,7 +5,7 @@
* It contains typing information for all components that exist in this project.
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
-import { AccordionGroupChangeEventDetail, ActionSheetAttributes, ActionSheetButton, AlertButton, AlertInput, AnimationBuilder, AutocompleteTypes, BreadcrumbCollapsedClickEventDetail, CheckboxChangeEventDetail, Color, ComponentProps, ComponentRef, DatetimeChangeEventDetail, DatetimePresentation, FrameworkDelegate, InputChangeEventDetail, ItemReorderEventDetail, LoadingAttributes, MenuChangeEventDetail, ModalAttributes, ModalBreakpointChangeEventDetail, ModalHandleBehavior, NavComponent, NavComponentWithProps, NavOptions, OverlayEventDetail, PickerAttributes, PickerButton, PickerColumn, PopoverAttributes, PopoverSize, PositionAlign, PositionReference, PositionSide, RadioGroupChangeEventDetail, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue, RefresherEventDetail, RouteID, RouterDirection, RouterEventDetail, RouterOutletOptions, RouteWrite, ScrollBaseDetail, ScrollDetail, SearchbarChangeEventDetail, SegmentButtonLayout, SegmentChangeEventDetail, SelectChangeEventDetail, SelectInterface, SelectPopoverOption, Side, SpinnerTypes, StyleEventDetail, SwipeGestureHandler, TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout, TextareaChangeEventDetail, TextFieldTypes, ToastButton, ToggleChangeEventDetail, TransitionDoneFn, TransitionInstruction, TriggerAction, ViewController } from "./interface";
+import { AccordionGroupChangeEventDetail, ActionSheetAttributes, ActionSheetButton, AlertButton, AlertInput, AnimationBuilder, AutocompleteTypes, BreadcrumbCollapsedClickEventDetail, CheckboxChangeEventDetail, Color, ComponentProps, ComponentRef, DatetimeChangeEventDetail, DatetimePresentation, FrameworkDelegate, InputChangeEventDetail, InputInputEventDetail, ItemReorderEventDetail, LoadingAttributes, MenuChangeEventDetail, ModalAttributes, ModalBreakpointChangeEventDetail, ModalHandleBehavior, NavComponent, NavComponentWithProps, NavOptions, OverlayEventDetail, PickerAttributes, PickerButton, PickerColumn, PopoverAttributes, PopoverSize, PositionAlign, PositionReference, PositionSide, RadioGroupChangeEventDetail, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue, RefresherEventDetail, RouteID, RouterDirection, RouterEventDetail, RouterOutletOptions, RouteWrite, ScrollBaseDetail, ScrollDetail, SearchbarChangeEventDetail, SegmentButtonLayout, SegmentChangeEventDetail, SelectChangeEventDetail, SelectInterface, SelectPopoverOption, Side, SpinnerTypes, StyleEventDetail, SwipeGestureHandler, TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout, TextareaChangeEventDetail, TextFieldTypes, ToastButton, ToggleChangeEventDetail, TransitionDoneFn, TransitionInstruction, TriggerAction, ViewController } from "./interface";
import { IonicSafeString } from "./utils/sanitization";
import { AlertAttributes } from "./components/alert/alert-interface";
import { CounterFormatter } from "./components/item/item-interface";
@@ -4898,7 +4898,7 @@ declare namespace LocalJSX {
*/
"onIonBlur"?: (event: IonInputCustomEvent) => void;
/**
- * Emitted when the value has changed.
+ * The `ionChange` event is fired for `` elements when the user modifies the element's value. Unlike the `ionInput` event, the `ionChange` event is not necessarily fired for each alteration to an element's value. Depending on the way the users interacts with the element, the `ionChange` event fires at a different moment: - When the user commits the change explicitly (e.g. by selecting a date from a date picker for ``, etc.). - When the element loses focus after its value has changed: for elements where the user's interaction is typing.
*/
"onIonChange"?: (event: IonInputCustomEvent) => void;
/**
@@ -4906,9 +4906,9 @@ declare namespace LocalJSX {
*/
"onIonFocus"?: (event: IonInputCustomEvent) => void;
/**
- * Emitted when a keyboard input occurred.
+ * The `ionInput` event fires when the `value` of an `` element has been changed. For elements that accept text input (`type=text`, `type=tel`, etc.), the interface is [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent); for others, the interface is [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event).
*/
- "onIonInput"?: (event: IonInputCustomEvent) => void;
+ "onIonInput"?: (event: IonInputCustomEvent) => void;
/**
* Emitted when the styles change.
*/
diff --git a/core/src/components/input/input-interface.ts b/core/src/components/input/input-interface.ts
index ce33636eb8..1e2f865e4b 100644
--- a/core/src/components/input/input-interface.ts
+++ b/core/src/components/input/input-interface.ts
@@ -2,6 +2,10 @@ export interface InputChangeEventDetail {
value: string | undefined | null;
}
+// We recognize that InputInput is not an ideal naming pattern for this type.
+// TODO (FW-2199): Explore renaming this type to something more appropriate.
+export type InputInputEventDetail = InputEvent | Event;
+
export interface InputCustomEvent extends CustomEvent {
detail: InputChangeEventDetail;
target: HTMLIonInputElement;
diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx
index 40d4dfdae7..488c100d68 100644
--- a/core/src/components/input/input.tsx
+++ b/core/src/components/input/input.tsx
@@ -6,6 +6,7 @@ import type {
AutocompleteTypes,
Color,
InputChangeEventDetail,
+ InputInputEventDetail,
StyleEventDetail,
TextFieldTypes,
} from '../../interface';
@@ -30,6 +31,17 @@ export class Input implements ComponentInterface {
private didBlurAfterEdit = false;
private inheritedAttributes: Attributes = {};
private isComposing = false;
+ /**
+ * If `true`, the user cleared the input by pressing the clear icon,
+ * within the session of the input being focused.
+ *
+ * This property is reset to `false` when the input is blurred.
+ */
+ private inputCleared = false;
+ /**
+ * The value of the input when the input is focused.
+ */
+ private focusedValue: string | number | null | undefined;
@State() hasFocus = false;
@@ -192,12 +204,27 @@ export class Input implements ComponentInterface {
@Prop({ mutable: true }) value?: string | number | null = '';
/**
- * Emitted when a keyboard input occurred.
+ * The `ionInput` event fires when the `value` of an `` element
+ * has been changed.
+ *
+ * For elements that accept text input (`type=text`, `type=tel`, etc.), the interface
+ * is [`InputEvent`](https://developer.mozilla.org/en-US/docs/Web/API/InputEvent); for others,
+ * the interface is [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event).
*/
- @Event() ionInput!: EventEmitter;
+ @Event() ionInput!: EventEmitter;
/**
- * Emitted when the value has changed.
+ * The `ionChange` event is fired for `` elements when the user
+ * modifies the element's value. Unlike the `ionInput` event, the `ionChange`
+ * event is not necessarily fired for each alteration to an element's value.
+ *
+ * Depending on the way the users interacts with the element, the `ionChange`
+ * event fires at a different moment:
+ * - When the user commits the change explicitly (e.g. by selecting a date
+ * from a date picker for ``, etc.).
+ * - When the element loses focus after its value has changed: for elements
+ * where the user's interaction is typing.
+ *
*/
@Event() ionChange!: EventEmitter;
@@ -244,7 +271,6 @@ export class Input implements ComponentInterface {
nativeInput.value = value;
}
this.emitStyle();
- this.ionChange.emit({ value: this.value == null ? this.value : this.value.toString() });
}
componentWillLoad() {
@@ -310,6 +336,18 @@ export class Input implements ComponentInterface {
return Promise.resolve(this.nativeInput!);
}
+ /**
+ * Emits an `ionChange` event.
+ *
+ * This API should be called for user committed changes.
+ * This API should not be used for external value changes.
+ */
+ private emitValueChange() {
+ const { value } = this;
+ const newValue = value == null ? value : value.toString();
+ this.ionChange.emit({ value: newValue });
+ }
+
private shouldClearOnEdit() {
const { type, clearOnEdit } = this;
return clearOnEdit === undefined ? type === 'password' : clearOnEdit;
@@ -338,16 +376,33 @@ export class Input implements ComponentInterface {
this.ionInput.emit(ev as InputEvent);
};
+ private onChange = () => {
+ this.emitValueChange();
+ };
+
private onBlur = (ev: FocusEvent) => {
this.hasFocus = false;
this.focusChanged();
this.emitStyle();
+ if (this.inputCleared) {
+ if (this.focusedValue !== this.value) {
+ /**
+ * Emits the `ionChange` event when the input value
+ * is different than the value when the input was focused.
+ */
+ this.emitValueChange();
+ }
+ this.focusedValue = undefined;
+ this.inputCleared = false;
+ }
+
this.ionBlur.emit(ev);
};
private onFocus = (ev: FocusEvent) => {
this.hasFocus = true;
+ this.focusedValue = this.value;
this.focusChanged();
this.emitStyle();
@@ -386,6 +441,9 @@ export class Input implements ComponentInterface {
}
this.value = '';
+ this.inputCleared = true;
+
+ this.ionInput.emit(ev);
/**
* This is needed for clearOnEdit
@@ -454,6 +512,7 @@ export class Input implements ComponentInterface {
type={this.type}
value={value}
onInput={this.onInput}
+ onChange={this.onChange}
onBlur={this.onBlur}
onFocus={this.onFocus}
onKeyDown={this.onKeydown}
diff --git a/core/src/components/input/test/input-events.e2e.ts b/core/src/components/input/test/input-events.e2e.ts
new file mode 100644
index 0000000000..528e46610f
--- /dev/null
+++ b/core/src/components/input/test/input-events.e2e.ts
@@ -0,0 +1,74 @@
+import { expect } from '@playwright/test';
+import { test } from '@utils/test/playwright';
+
+test.describe('input: events: ionChange', () => {
+ test.describe('when the input is blurred', () => {
+ test.describe('should emit', () => {
+ test('if the value has changed', async ({ page }) => {
+ await page.setContent(``);
+
+ const nativeInput = page.locator('ion-input input');
+ const ionChangeSpy = await page.spyOnEvent('ionChange');
+
+ await nativeInput.type('new value', { delay: 100 });
+ // Value change is not emitted until the control is blurred.
+ await nativeInput.evaluate((e) => e.blur());
+
+ await ionChangeSpy.next();
+
+ expect(ionChangeSpy).toHaveReceivedEventDetail({ value: 'new value' });
+ });
+ });
+
+ test.describe('should not emit', () => {
+ test('if the value has not changed', async ({ page }) => {
+ await page.setContent(``);
+
+ const ionChangeSpy = await page.spyOnEvent('ionChange');
+ const nativeInput = page.locator('ion-input input');
+
+ await nativeInput.type('new value', { delay: 100 });
+
+ await page.click('ion-input .input-clear-icon');
+
+ await nativeInput.evaluate((e) => e.blur());
+
+ expect(ionChangeSpy.events.length).toBe(0);
+ });
+
+ test('if the value is set programmatically', async ({ page }) => {
+ await page.setContent(``);
+
+ const input = page.locator('ion-input');
+ const ionChangeSpy = await page.spyOnEvent('ionChange');
+
+ await input.evaluate((el: HTMLIonInputElement) => {
+ el.value = 'new value';
+ });
+
+ expect(ionChangeSpy.events.length).toBe(0);
+
+ // Update the value again to make sure it doesn't emit a second time
+ await input.evaluate((el: HTMLIonInputElement) => {
+ el.value = 'new value 2';
+ });
+
+ expect(ionChangeSpy.events.length).toBe(0);
+ });
+ });
+ });
+});
+
+test.describe('input: events: ionInput', () => {
+ test.describe('should emit', () => {
+ test('when the input is cleared', async ({ page }) => {
+ await page.setContent(``);
+
+ const ionInputSpy = await page.spyOnEvent('ionInput');
+
+ await page.click('ion-input .input-clear-icon');
+
+ expect(ionInputSpy).toHaveReceivedEventDetail({ isTrusted: true });
+ });
+ });
+});
diff --git a/core/src/components/item/item.tsx b/core/src/components/item/item.tsx
index ee70493042..197b766f34 100644
--- a/core/src/components/item/item.tsx
+++ b/core/src/components/item/item.tsx
@@ -8,7 +8,7 @@ import type { AnchorInterface, ButtonInterface } from '../../utils/element-inter
import { raf } from '../../utils/helpers';
import { printIonError } from '../../utils/logging';
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
-import type { InputChangeEventDetail } from '../input/input-interface';
+import type { InputInputEventDetail } from '../input/input-interface';
import type { CounterFormatter } from './item-interface';
@@ -149,8 +149,8 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
this.updateCounterOutput(this.getFirstInput());
}
- @Listen('ionChange')
- handleIonChange(ev: CustomEvent) {
+ @Listen('ionInput')
+ handleIonInput(ev: CustomEvent) {
if (this.counter && ev.target === this.getFirstInput()) {
this.updateCounterOutput(ev.target as HTMLIonInputElement | HTMLIonTextareaElement);
}
diff --git a/core/src/components/item/test/counter/item.e2e.ts b/core/src/components/item/test/counter/item.e2e.ts
index 7600d0fe2c..06135a659c 100644
--- a/core/src/components/item/test/counter/item.e2e.ts
+++ b/core/src/components/item/test/counter/item.e2e.ts
@@ -24,7 +24,7 @@ test.describe('item: counter', () => {
test('should format on input', async ({ page }) => {
const input = page.locator('#customFormatter ion-input');
- await input.click();
+ await page.click('#customFormatter ion-input input');
await input.type('abcde');
await page.waitForChanges();