mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-22 05:21:52 +08:00
feat(inputs) debounce input and change events (#13764)
* feat(helpers) add debounce helper * feat(searchbar) debounce ionInput * feat(range) debounce ionChange * feat(input) debouce ionInput * feat(textarea) debounce ionInput * feat(range) make debounceChange private * feat(searchbar) make debounceInput private * feat(inputs) change default debounce to 0 in input/textarea
This commit is contained in:
2
packages/core/src/components.d.ts
vendored
2
packages/core/src/components.d.ts
vendored
@ -1155,6 +1155,7 @@ declare global {
|
|||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
clearInput?: boolean;
|
clearInput?: boolean;
|
||||||
clearOnEdit?: boolean;
|
clearOnEdit?: boolean;
|
||||||
|
debounce?: number;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
inputmode?: string;
|
inputmode?: string;
|
||||||
max?: string;
|
max?: string;
|
||||||
@ -2880,6 +2881,7 @@ declare global {
|
|||||||
autofocus?: boolean;
|
autofocus?: boolean;
|
||||||
clearOnEdit?: boolean;
|
clearOnEdit?: boolean;
|
||||||
cols?: number;
|
cols?: number;
|
||||||
|
debounce?: number;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
maxlength?: number;
|
maxlength?: number;
|
||||||
minlength?: number;
|
minlength?: number;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Prop, PropDidChange } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Prop, PropDidChange } from '@stencil/core';
|
||||||
|
|
||||||
|
import { debounce } from '../../utils/helpers';
|
||||||
import { createThemedClasses } from '../../utils/theme';
|
import { createThemedClasses } from '../../utils/theme';
|
||||||
import { InputComponent } from './input-base';
|
import { InputComponent } from './input-base';
|
||||||
|
|
||||||
@ -23,6 +24,11 @@ export class Input implements InputComponent {
|
|||||||
|
|
||||||
@Element() private el: HTMLElement;
|
@Element() private el: HTMLElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @output {Event} Emitted when the input value has changed.
|
||||||
|
*/
|
||||||
|
@Event() ionInput: EventEmitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @output {Event} Emitted when the styles change.
|
* @output {Event} Emitted when the styles change.
|
||||||
*/
|
*/
|
||||||
@ -83,6 +89,18 @@ export class Input implements InputComponent {
|
|||||||
*/
|
*/
|
||||||
@Prop({ mutable: true }) clearOnEdit: boolean;
|
@Prop({ mutable: true }) clearOnEdit: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @input {number} Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke. Default `0`.
|
||||||
|
*/
|
||||||
|
@Prop() debounce: number = 0;
|
||||||
|
@PropDidChange('debounce')
|
||||||
|
private debounceInput() {
|
||||||
|
this.ionInput.emit = debounce(
|
||||||
|
this.ionInput.emit.bind(this.ionInput),
|
||||||
|
this.debounce
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @input {boolean} If true, the user cannot interact with the input. Defaults to `false`.
|
* @input {boolean} If true, the user cannot interact with the input. Defaults to `false`.
|
||||||
*/
|
*/
|
||||||
@ -192,6 +210,7 @@ export class Input implements InputComponent {
|
|||||||
|
|
||||||
|
|
||||||
componentDidLoad() {
|
componentDidLoad() {
|
||||||
|
this.debounceInput();
|
||||||
this.emitStyle();
|
this.emitStyle();
|
||||||
|
|
||||||
// By default, password inputs clear after focus when they have content
|
// By default, password inputs clear after focus when they have content
|
||||||
@ -225,6 +244,7 @@ export class Input implements InputComponent {
|
|||||||
|
|
||||||
inputChanged(ev: any) {
|
inputChanged(ev: any) {
|
||||||
this.value = ev.target && ev.target.value;
|
this.value = ev.target && ev.target.value;
|
||||||
|
this.ionInput.emit(ev);
|
||||||
this.emitStyle();
|
this.emitStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,15 +262,15 @@ export class Input implements InputComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputKeydown() {
|
inputKeydown(ev: any) {
|
||||||
this.checkClearOnEdit();
|
this.checkClearOnEdit(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if we need to clear the text input if clearOnEdit is enabled
|
* Check if we need to clear the text input if clearOnEdit is enabled
|
||||||
*/
|
*/
|
||||||
checkClearOnEdit() {
|
checkClearOnEdit(ev: any) {
|
||||||
if (!this.clearOnEdit) {
|
if (!this.clearOnEdit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -258,15 +278,16 @@ export class Input implements InputComponent {
|
|||||||
// Did the input value change after it was blurred and edited?
|
// Did the input value change after it was blurred and edited?
|
||||||
if (this.didBlurAfterEdit && this.hasValue()) {
|
if (this.didBlurAfterEdit && this.hasValue()) {
|
||||||
// Clear the input
|
// Clear the input
|
||||||
this.clearTextInput();
|
this.clearTextInput(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the flag
|
// Reset the flag
|
||||||
this.didBlurAfterEdit = false;
|
this.didBlurAfterEdit = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTextInput() {
|
clearTextInput(ev: any) {
|
||||||
this.value = '';
|
this.value = '';
|
||||||
|
this.ionInput.emit(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasFocus(): boolean {
|
hasFocus(): boolean {
|
||||||
|
@ -47,6 +47,11 @@ boolean
|
|||||||
boolean
|
boolean
|
||||||
|
|
||||||
|
|
||||||
|
#### debounce
|
||||||
|
|
||||||
|
number
|
||||||
|
|
||||||
|
|
||||||
#### disabled
|
#### disabled
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
@ -179,6 +184,11 @@ boolean
|
|||||||
boolean
|
boolean
|
||||||
|
|
||||||
|
|
||||||
|
#### debounce
|
||||||
|
|
||||||
|
number
|
||||||
|
|
||||||
|
|
||||||
#### disabled
|
#### disabled
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
@ -277,6 +287,9 @@ string
|
|||||||
#### ionFocus
|
#### ionFocus
|
||||||
|
|
||||||
|
|
||||||
|
#### ionInput
|
||||||
|
|
||||||
|
|
||||||
#### ionStyle
|
#### ionStyle
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange, State } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange, State } from '@stencil/core';
|
||||||
import { BaseInputComponent, GestureDetail } from '../../index';
|
import { BaseInputComponent, GestureDetail } from '../../index';
|
||||||
import { clamp } from '../../utils/helpers';
|
import { clamp, debounce } from '../../utils/helpers';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
tag: 'ion-range',
|
tag: 'ion-range',
|
||||||
@ -75,6 +75,13 @@ export class Range implements BaseInputComponent {
|
|||||||
* `ionChange` event after each change in the range value. Default `0`.
|
* `ionChange` event after each change in the range value. Default `0`.
|
||||||
*/
|
*/
|
||||||
@Prop() debounce: number = 0;
|
@Prop() debounce: number = 0;
|
||||||
|
@PropDidChange('debounce')
|
||||||
|
private debounceChange() {
|
||||||
|
this.ionChange.emit = debounce(
|
||||||
|
this.ionChange.emit.bind(this.ionChange),
|
||||||
|
this.debounce
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @input {boolean} If true, the user cannot interact with the range. Default false.
|
* @input {boolean} If true, the user cannot interact with the range. Default false.
|
||||||
@ -126,13 +133,14 @@ export class Range implements BaseInputComponent {
|
|||||||
|
|
||||||
@PropDidChange('value')
|
@PropDidChange('value')
|
||||||
protected valueChanged(val: boolean) {
|
protected valueChanged(val: boolean) {
|
||||||
setTimeout(() => this.ionChange.emit({value: val}), this.debounce);
|
this.ionChange.emit({value: val});
|
||||||
this.emitStyle();
|
this.emitStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillLoad() {
|
componentWillLoad() {
|
||||||
this.inputUpdated();
|
this.inputUpdated();
|
||||||
this.createTicks();
|
this.createTicks();
|
||||||
|
this.debounceChange();
|
||||||
this.emitStyle();
|
this.emitStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Prop, State } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Prop, PropDidChange, State } from '@stencil/core';
|
||||||
|
import { debounce } from '../../utils/helpers';
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -86,6 +87,13 @@ export class Searchbar {
|
|||||||
* @input {number} Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke. Default `250`.
|
* @input {number} Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke. Default `250`.
|
||||||
*/
|
*/
|
||||||
@Prop({ mutable: true }) debounce: number = 250;
|
@Prop({ mutable: true }) debounce: number = 250;
|
||||||
|
@PropDidChange('debounce')
|
||||||
|
private debounceInput() {
|
||||||
|
this.ionInput.emit = debounce(
|
||||||
|
this.ionInput.emit.bind(this.ionInput),
|
||||||
|
this.debounce
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @input {string} Set the input's placeholder. Default `"Search"`.
|
* @input {string} Set the input's placeholder. Default `"Search"`.
|
||||||
@ -115,6 +123,7 @@ export class Searchbar {
|
|||||||
|
|
||||||
componentDidLoad() {
|
componentDidLoad() {
|
||||||
this.positionElements();
|
this.positionElements();
|
||||||
|
this.debounceInput();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,9 +162,7 @@ export class Searchbar {
|
|||||||
*/
|
*/
|
||||||
inputChanged(ev: any) {
|
inputChanged(ev: any) {
|
||||||
this.value = ev.target.value;
|
this.value = ev.target.value;
|
||||||
setTimeout(() => {
|
this.ionInput.emit(ev);
|
||||||
this.ionInput.emit(ev);
|
|
||||||
}, this.debounce);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputUpdated() {
|
inputUpdated() {
|
||||||
|
@ -66,6 +66,11 @@ boolean
|
|||||||
number
|
number
|
||||||
|
|
||||||
|
|
||||||
|
#### debounce
|
||||||
|
|
||||||
|
number
|
||||||
|
|
||||||
|
|
||||||
#### disabled
|
#### disabled
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
@ -148,6 +153,11 @@ boolean
|
|||||||
number
|
number
|
||||||
|
|
||||||
|
|
||||||
|
#### debounce
|
||||||
|
|
||||||
|
number
|
||||||
|
|
||||||
|
|
||||||
#### disabled
|
#### disabled
|
||||||
|
|
||||||
boolean
|
boolean
|
||||||
@ -211,6 +221,9 @@ string
|
|||||||
#### ionFocus
|
#### ionFocus
|
||||||
|
|
||||||
|
|
||||||
|
#### ionInput
|
||||||
|
|
||||||
|
|
||||||
#### ionStyle
|
#### ionStyle
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Component, Element, Event, EventEmitter, Prop, PropDidChange } from '@stencil/core';
|
import { Component, Element, Event, EventEmitter, Prop, PropDidChange } from '@stencil/core';
|
||||||
|
|
||||||
|
import { debounce } from '../../utils/helpers';
|
||||||
import { createThemedClasses } from '../../utils/theme';
|
import { createThemedClasses } from '../../utils/theme';
|
||||||
import { TextareaComponent } from '../input/input-base';
|
import { TextareaComponent } from '../input/input-base';
|
||||||
|
|
||||||
@ -29,6 +30,11 @@ export class Textarea implements TextareaComponent {
|
|||||||
|
|
||||||
@Element() private el: HTMLElement;
|
@Element() private el: HTMLElement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @output {Event} Emitted when the input value has changed.
|
||||||
|
*/
|
||||||
|
@Event() ionInput: EventEmitter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @output {Event} Emitted when the styles change.
|
* @output {Event} Emitted when the styles change.
|
||||||
*/
|
*/
|
||||||
@ -64,6 +70,18 @@ export class Textarea implements TextareaComponent {
|
|||||||
*/
|
*/
|
||||||
@Prop({ mutable: true }) clearOnEdit: boolean;
|
@Prop({ mutable: true }) clearOnEdit: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @input {number} Set the amount of time, in milliseconds, to wait to trigger the `ionInput` event after each keystroke. Default `0`.
|
||||||
|
*/
|
||||||
|
@Prop() debounce: number = 0;
|
||||||
|
@PropDidChange('debounce')
|
||||||
|
private debounceInput() {
|
||||||
|
this.ionInput.emit = debounce(
|
||||||
|
this.ionInput.emit.bind(this.ionInput),
|
||||||
|
this.debounce
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @input {boolean} If true, the user cannot interact with the textarea. Defaults to `false`.
|
* @input {boolean} If true, the user cannot interact with the textarea. Defaults to `false`.
|
||||||
*/
|
*/
|
||||||
@ -141,6 +159,7 @@ export class Textarea implements TextareaComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidLoad() {
|
componentDidLoad() {
|
||||||
|
this.debounceInput();
|
||||||
this.emitStyle();
|
this.emitStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,8 +179,9 @@ export class Textarea implements TextareaComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
clearTextInput() {
|
clearTextInput(ev: any) {
|
||||||
this.value = '';
|
this.value = '';
|
||||||
|
this.ionInput.emit(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
inputBlurred(ev: any) {
|
inputBlurred(ev: any) {
|
||||||
@ -173,6 +193,7 @@ export class Textarea implements TextareaComponent {
|
|||||||
|
|
||||||
inputChanged(ev: any) {
|
inputChanged(ev: any) {
|
||||||
this.value = ev.target && ev.target.value;
|
this.value = ev.target && ev.target.value;
|
||||||
|
this.ionInput.emit(ev);
|
||||||
this.emitStyle();
|
this.emitStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,14 +204,14 @@ export class Textarea implements TextareaComponent {
|
|||||||
this.emitStyle();
|
this.emitStyle();
|
||||||
}
|
}
|
||||||
|
|
||||||
inputKeydown() {
|
inputKeydown(ev: any) {
|
||||||
this.checkClearOnEdit();
|
this.checkClearOnEdit(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if we need to clear the text input if clearOnEdit is enabled
|
* Check if we need to clear the text input if clearOnEdit is enabled
|
||||||
*/
|
*/
|
||||||
checkClearOnEdit() {
|
checkClearOnEdit(ev: any) {
|
||||||
if (!this.clearOnEdit) {
|
if (!this.clearOnEdit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -198,7 +219,7 @@ export class Textarea implements TextareaComponent {
|
|||||||
// Did the input value change after it was blurred and edited?
|
// Did the input value change after it was blurred and edited?
|
||||||
if (this.didBlurAfterEdit && this.hasValue()) {
|
if (this.didBlurAfterEdit && this.hasValue()) {
|
||||||
// Clear the input
|
// Clear the input
|
||||||
this.clearTextInput();
|
this.clearTextInput(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset the flag
|
// Reset the flag
|
||||||
|
@ -295,3 +295,11 @@ export function domControllerAsync(domControllerFunction: Function, callback?: F
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function debounce(func: Function, wait: number = 250) {
|
||||||
|
let timer: number;
|
||||||
|
return (...args: any[]): void => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(func, wait, ...args);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user