Skip to content

Commit

Permalink
fix(input, searchbar, textarea): ensure nativeInput is always availab…
Browse files Browse the repository at this point in the history
…le (#28362)

Issue number: resolves #28283 

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

`getInputElement()` is used to access the native input. If the component
has yet to render, then the function will return `undefined`. This
happens mostly when using `ref` on React.

```tsx
<IonInput ref={async input => {
  const nativeInput = await input.getInputElement();
  // nativeInput is undefined
}} />
```

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- `getInputElement()` will wait to return once the component is ready.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `7.5.1-dev.11697488622.175c9183`
  • Loading branch information
thetaPC authored Oct 23, 2023
1 parent 15a0225 commit 2b015b2
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 6 deletions.
17 changes: 15 additions & 2 deletions core/src/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, for
import type { LegacyFormController, NotchController } from '@utils/forms';
import { createLegacyFormController, createNotchController } from '@utils/forms';
import type { Attributes } from '@utils/helpers';
import { inheritAriaAttributes, debounceEvent, findItemLabel, inheritAttributes } from '@utils/helpers';
import {
inheritAriaAttributes,
debounceEvent,
findItemLabel,
inheritAttributes,
componentOnReady,
} from '@utils/helpers';
import { printIonWarning } from '@utils/logging';
import { createSlotMutationController } from '@utils/slot-mutation-controller';
import type { SlotMutationController } from '@utils/slot-mutation-controller';
Expand Down Expand Up @@ -430,7 +436,14 @@ export class Input implements ComponentInterface {
* Returns the native `<input>` element used under the hood.
*/
@Method()
getInputElement(): Promise<HTMLInputElement> {
async getInputElement(): Promise<HTMLInputElement> {
/**
* If this gets called in certain early lifecycle hooks (ex: Vue onMounted),
* nativeInput won't be defined yet with the custom elements build, so wait for it to load in.
*/
if (!this.nativeInput) {
await new Promise((resolve) => componentOnReady(this.el, resolve));
}
return Promise.resolve(this.nativeInput!);
}

Expand Down
11 changes: 9 additions & 2 deletions core/src/components/searchbar/searchbar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Method, Prop, State, Watch, forceUpdate, h } from '@stencil/core';
import { debounceEvent, raf } from '@utils/helpers';
import { debounceEvent, raf, componentOnReady } from '@utils/helpers';
import { isRTL } from '@utils/rtl';
import { createColorClasses } from '@utils/theme';
import { arrowBackSharp, closeCircle, closeSharp, searchOutline, searchSharp } from 'ionicons/icons';
Expand Down Expand Up @@ -269,7 +269,14 @@ export class Searchbar implements ComponentInterface {
* Returns the native `<input>` element used under the hood.
*/
@Method()
getInputElement(): Promise<HTMLInputElement> {
async getInputElement(): Promise<HTMLInputElement> {
/**
* If this gets called in certain early lifecycle hooks (ex: Vue onMounted),
* nativeInput won't be defined yet with the custom elements build, so wait for it to load in.
*/
if (!this.nativeInput) {
await new Promise((resolve) => componentOnReady(this.el, resolve));
}
return Promise.resolve(this.nativeInput!);
}

Expand Down
17 changes: 15 additions & 2 deletions core/src/components/textarea/textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@ import {
import type { LegacyFormController, NotchController } from '@utils/forms';
import { createLegacyFormController, createNotchController } from '@utils/forms';
import type { Attributes } from '@utils/helpers';
import { inheritAriaAttributes, debounceEvent, findItemLabel, inheritAttributes } from '@utils/helpers';
import {
inheritAriaAttributes,
debounceEvent,
findItemLabel,
inheritAttributes,
componentOnReady,
} from '@utils/helpers';
import { printIonWarning } from '@utils/logging';
import { createSlotMutationController } from '@utils/slot-mutation-controller';
import type { SlotMutationController } from '@utils/slot-mutation-controller';
Expand Down Expand Up @@ -378,7 +384,14 @@ export class Textarea implements ComponentInterface {
* Returns the native `<textarea>` element used under the hood.
*/
@Method()
getInputElement(): Promise<HTMLTextAreaElement> {
async getInputElement(): Promise<HTMLTextAreaElement> {
/**
* If this gets called in certain early lifecycle hooks (ex: Vue onMounted),
* nativeInput won't be defined yet with the custom elements build, so wait for it to load in.
*/
if (!this.nativeInput) {
await new Promise((resolve) => componentOnReady(this.el, resolve));
}
return Promise.resolve(this.nativeInput!);
}

Expand Down

0 comments on commit 2b015b2

Please sign in to comment.