mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-17 02:31:34 +08:00
fix(button): submit form when pressing enter key (#27790)
Issue number: resolves #19368 --------- <!-- 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? The form submits when clicking on the `ion-button`. However, users cannot submit the form when focused on a form element and pressing the `enter` button. This does not follow the behavior of the native button on a form. ## What is the new behavior? <!-- Please describe the behavior or changes that are being added by this PR. --> - Form now submits when a form element is focused and the `enter` button is pressed. - It also submits regardless of the amount of form elements present. - Form will not submit if the `ion-button` is disabled. ## 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. --> N/A
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
import type { ComponentInterface, EventEmitter } from '@stencil/core';
|
||||
import { Component, Element, Event, Host, Prop, h } from '@stencil/core';
|
||||
import { Component, Element, Event, Host, Prop, Watch, h } from '@stencil/core';
|
||||
import type { AnchorInterface, ButtonInterface } from '@utils/element-interface';
|
||||
import type { Attributes } from '@utils/helpers';
|
||||
import { inheritAriaAttributes, hasShadowDom } from '@utils/helpers';
|
||||
@ -32,6 +32,8 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
private inItem = false;
|
||||
private inListHeader = false;
|
||||
private inToolbar = false;
|
||||
private formButtonEl: HTMLButtonElement | null = null;
|
||||
private formEl: HTMLFormElement | null = null;
|
||||
private inheritedAttributes: Attributes = {};
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
@ -52,6 +54,13 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
* If `true`, the user cannot interact with the button.
|
||||
*/
|
||||
@Prop({ reflect: true }) disabled = false;
|
||||
@Watch('disabled')
|
||||
disabledChanged() {
|
||||
const { disabled } = this;
|
||||
if (this.formButtonEl) {
|
||||
this.formButtonEl.disabled = disabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to `"block"` for a full-width button or to `"full"` for a full-width button
|
||||
@ -144,6 +153,22 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
*/
|
||||
@Event() ionBlur!: EventEmitter<void>;
|
||||
|
||||
connectedCallback(): void {
|
||||
// Allow form to be submitted through `ion-button`
|
||||
if (this.type !== 'button' && hasShadowDom(this.el)) {
|
||||
this.formEl = this.findForm();
|
||||
if (this.formEl) {
|
||||
// Create a hidden native button inside of the form
|
||||
this.formButtonEl = document.createElement('button');
|
||||
this.formButtonEl.type = this.type;
|
||||
this.formButtonEl.style.display = 'none';
|
||||
// Only submit if the button is not disabled.
|
||||
this.formButtonEl.disabled = this.disabled;
|
||||
this.formEl.appendChild(this.formButtonEl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
this.inToolbar = !!this.el.closest('ion-buttons');
|
||||
this.inListHeader = !!this.el.closest('ion-list-header');
|
||||
@ -177,12 +202,63 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
return form;
|
||||
}
|
||||
if (typeof form === 'string') {
|
||||
const el = document.getElementById(form);
|
||||
if (el instanceof HTMLFormElement) {
|
||||
return el;
|
||||
// Check if the string provided is a form id.
|
||||
const el: HTMLElement | null = document.getElementById(form);
|
||||
if (el) {
|
||||
if (el instanceof HTMLFormElement) {
|
||||
return el;
|
||||
} else {
|
||||
/**
|
||||
* The developer specified a string for the form attribute, but the
|
||||
* element with that id is not a form element.
|
||||
*/
|
||||
printIonWarning(
|
||||
`Form with selector: "#${form}" could not be found. Verify that the id is attached to a <form> element.`,
|
||||
this.el
|
||||
);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* The developer specified a string for the form attribute, but the
|
||||
* element with that id could not be found in the DOM.
|
||||
*/
|
||||
printIonWarning(
|
||||
`Form with selector: "#${form}" could not be found. Verify that the id is correct and the form is rendered in the DOM.`,
|
||||
this.el
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
if (form !== undefined) {
|
||||
/**
|
||||
* The developer specified a HTMLElement for the form attribute,
|
||||
* but the element is not a HTMLFormElement.
|
||||
* This will also catch if the developer tries to pass in null
|
||||
* as the form attribute.
|
||||
*/
|
||||
printIonWarning(
|
||||
`The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.`,
|
||||
this.el
|
||||
);
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* If the form element is not set, the button may be inside
|
||||
* of a form element. Query the closest form element to the button.
|
||||
*/
|
||||
return this.el.closest('form');
|
||||
}
|
||||
|
||||
private submitForm(ev: Event) {
|
||||
// this button wants to specifically submit a form
|
||||
// climb up the dom to see if we're in a <form>
|
||||
// and if so, then use JS to submit it
|
||||
if (this.formEl && this.formButtonEl) {
|
||||
ev.preventDefault();
|
||||
|
||||
this.formButtonEl.click();
|
||||
}
|
||||
}
|
||||
|
||||
private handleClick = (ev: Event) => {
|
||||
@ -190,49 +266,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
|
||||
if (this.type === 'button') {
|
||||
openURL(this.href, ev, this.routerDirection, this.routerAnimation);
|
||||
} else if (hasShadowDom(el)) {
|
||||
// this button wants to specifically submit a form
|
||||
// climb up the dom to see if we're in a <form>
|
||||
// and if so, then use JS to submit it
|
||||
let formEl = this.findForm();
|
||||
const { form } = this;
|
||||
|
||||
if (!formEl && form !== undefined) {
|
||||
/**
|
||||
* The developer specified a form selector for
|
||||
* the button to submit, but it was not found.
|
||||
*/
|
||||
if (typeof form === 'string') {
|
||||
printIonWarning(
|
||||
`Form with selector: "#${form}" could not be found. Verify that the id is correct and the form is rendered in the DOM.`,
|
||||
el
|
||||
);
|
||||
} else {
|
||||
printIonWarning(
|
||||
`The provided "form" element is invalid. Verify that the form is a HTMLFormElement and rendered in the DOM.`,
|
||||
el
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formEl) {
|
||||
/**
|
||||
* If the form element is not set, the button may be inside
|
||||
* of a form element. Query the closest form element to the button.
|
||||
*/
|
||||
formEl = el.closest('form');
|
||||
}
|
||||
|
||||
if (formEl) {
|
||||
ev.preventDefault();
|
||||
|
||||
const fakeButton = document.createElement('button');
|
||||
fakeButton.type = this.type;
|
||||
fakeButton.style.display = 'none';
|
||||
formEl.appendChild(fakeButton);
|
||||
fakeButton.click();
|
||||
fakeButton.remove();
|
||||
}
|
||||
this.submitForm(ev);
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user