Skip to content

Commit a0fa4d2

Browse files
committed
fix(pat-inject): Don't submit forms with invalid data.
Fix a problem with pat-inject and pat-validation where forms with invalid data could be submitted and the submit button wasn't inactive. The problem was fixed in two ways: - pat-inject now has a check for browser-native form validation. Invalid forms would not be submitted. - pat-inject now waits a tick before it get's initialized. Modern BasePattern based patterns including pat-validation are all deferred for 1 tick by design. pat-inject, being and older Pattern isn't deferred and thus initialized before pat-inject. It's initialized before pat-validation even though we have some Pattern initialization reorganization code in place - pat-inject not being deferred will have it's event handlers always run before any others. But now, with the 1-tick defer in place, pat-inject's event handlers are run in the correct order - after pat-validation form validation which in case of an invalid form would also deactivate the submit button.
1 parent 1d9a6c8 commit a0fa4d2

File tree

6 files changed

+230
-105
lines changed

6 files changed

+230
-105
lines changed

src/pat/inject/inject.js

Lines changed: 60 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import "../../core/jquery-ext"; // for findInclusive
2+
import "../../core/polyfills"; // SubmitEvent.submitter for Safari < 15.4 and jsDOM
23
import $ from "jquery";
34
import ajax from "../ajax/ajax";
45
import dom from "../../core/dom";
@@ -47,7 +48,14 @@ const inject = {
4748
trigger: "a.pat-inject, form.pat-inject, .pat-subform.pat-inject",
4849
parser: parser,
4950

50-
init($el, opts) {
51+
async init($el, opts) {
52+
// We need to wait a tick. Modern BasePattern based patterns like
53+
// pat-validation do always wait a tick before initializing. The
54+
// patterns registry always initializes pat-validation first but since
55+
// it is waiting a tick the event handlers are registerd after the ones
56+
// from pat-inject. Waiting a tick in pat-inject solves this -
57+
// pat-validation's event handlers are initialized first.
58+
await utils.timeout(1);
5159
const cfgs = this.extractConfig($el, opts);
5260
if (cfgs.some((e) => e.history === "record") && !("pushState" in history)) {
5361
// if the injection shall add a history entry and HTML5 pushState
@@ -95,21 +103,29 @@ const inject = {
95103
}
96104
});
97105
// setup event handlers
98-
if ($el.is("form")) {
99-
$el.on("submit.pat-inject", this.onTrigger.bind(this))
100-
.on(
101-
"click.pat-inject",
102-
`[type=submit]:not([formaction]),
103-
button:not([formaction]):not([type=button])`,
104-
ajax.onClickSubmit
105-
)
106-
.on(
107-
"click.pat-inject",
108-
`[type=submit][formaction],
109-
[type=image][formaction],
110-
button[formaction]:not([type=button])`,
111-
this.onFormActionSubmit.bind(this)
106+
if ($el[0]?.tagName === "FORM") {
107+
events.add_event_listener(
108+
$el[0],
109+
"submit",
110+
"pat-inject--form-submit",
111+
(e) => {
112+
this.onTrigger(e);
113+
}
114+
);
115+
for (const button of $el[0].querySelectorAll(
116+
"[type=submit], button:not([type=button]), [type=image]"
117+
)) {
118+
events.add_event_listener(
119+
button,
120+
"click",
121+
"pat-inject--form-submit-click",
122+
(e) => {
123+
// make sure the submitting button is sent
124+
// with the form
125+
ajax.onClickSubmit(e);
126+
}
112127
);
128+
}
113129
} else if ($el.is(".pat-subform")) {
114130
log.debug("Initializing subform with injection");
115131
} else {
@@ -165,37 +181,40 @@ const inject = {
165181
/* Injection has been triggered, either via form submission or a
166182
* link has been clicked.
167183
*/
168-
const $el = $(e.currentTarget);
169-
const cfgs = $el.data("pat-inject");
170-
if ($el.is("form")) {
171-
$(cfgs).each((i, v) => {
172-
v.params = $.param($el.serializeArray());
173-
});
174-
}
184+
185+
// Prevent the original event from doing it's work.
186+
// We want an AJAX request instead.
175187
e.preventDefault && e.preventDefault();
176-
$el.trigger("patterns-inject-triggered");
177-
this.execute(cfgs, $el);
178-
},
179188

180-
onFormActionSubmit(e) {
181-
ajax.onClickSubmit(e); // make sure the submitting button is sent with the form
189+
const $el = $(e.currentTarget);
190+
let cfgs = $el.data("pat-inject");
191+
if ($el[0].tagName === "FORM" && e.type === "submit") {
192+
if ($el[0].matches(":invalid")) {
193+
// Do not submit invalid forms.
194+
// Works with native form validation and with pat-validation.
195+
log.debug("Form is invalid, aborting");
196+
return;
197+
}
182198

183-
const $button = $(e.target);
184-
const formaction = $button.attr("formaction");
185-
const $form = $button.parents(".pat-inject").first();
186-
const opts = {
187-
url: formaction,
188-
};
189-
const $cfg_node = $button.closest("[data-pat-inject]");
190-
const cfgs = this.extractConfig($cfg_node, opts);
199+
const submitter = e.submitter;
200+
const formaction = submitter?.getAttribute("formaction");
201+
if (formaction) {
202+
const opts = {
203+
url: formaction,
204+
};
191205

192-
$(cfgs).each((i, v) => {
193-
v.params = $.param($form.serializeArray());
194-
});
206+
// Support custom data-pat-inject on formaction buttons.
207+
const cfg_node = submitter.closest("[data-pat-inject]");
208+
cfgs = this.extractConfig($(cfg_node), opts);
209+
}
195210

196-
e.preventDefault();
197-
$form.trigger("patterns-inject-triggered");
198-
this.execute(cfgs, $form);
211+
for (const cfg of cfgs) {
212+
cfg.params = $.param($el.serializeArray());
213+
}
214+
}
215+
216+
$el.trigger("patterns-inject-triggered");
217+
this.execute(cfgs, $el);
199218
},
200219

201220
submitSubform($sub) {

0 commit comments

Comments
 (0)