Skip to content

Commit

Permalink
fix(pat-inject): Don't submit forms with invalid data.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
thet committed Oct 15, 2023
1 parent 1d9a6c8 commit a0fa4d2
Show file tree
Hide file tree
Showing 6 changed files with 230 additions and 105 deletions.
101 changes: 60 additions & 41 deletions src/pat/inject/inject.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import "../../core/jquery-ext"; // for findInclusive
import "../../core/polyfills"; // SubmitEvent.submitter for Safari < 15.4 and jsDOM
import $ from "jquery";
import ajax from "../ajax/ajax";
import dom from "../../core/dom";
Expand Down Expand Up @@ -47,7 +48,14 @@ const inject = {
trigger: "a.pat-inject, form.pat-inject, .pat-subform.pat-inject",
parser: parser,

init($el, opts) {
async init($el, opts) {
// We need to wait a tick. Modern BasePattern based patterns like
// pat-validation do always wait a tick before initializing. The
// patterns registry always initializes pat-validation first but since
// it is waiting a tick the event handlers are registerd after the ones
// from pat-inject. Waiting a tick in pat-inject solves this -
// pat-validation's event handlers are initialized first.
await utils.timeout(1);
const cfgs = this.extractConfig($el, opts);
if (cfgs.some((e) => e.history === "record") && !("pushState" in history)) {
// if the injection shall add a history entry and HTML5 pushState
Expand Down Expand Up @@ -95,21 +103,29 @@ const inject = {
}
});
// setup event handlers
if ($el.is("form")) {
$el.on("submit.pat-inject", this.onTrigger.bind(this))
.on(
"click.pat-inject",
`[type=submit]:not([formaction]),
button:not([formaction]):not([type=button])`,
ajax.onClickSubmit
)
.on(
"click.pat-inject",
`[type=submit][formaction],
[type=image][formaction],
button[formaction]:not([type=button])`,
this.onFormActionSubmit.bind(this)
if ($el[0]?.tagName === "FORM") {
events.add_event_listener(
$el[0],
"submit",
"pat-inject--form-submit",
(e) => {
this.onTrigger(e);
}
);
for (const button of $el[0].querySelectorAll(
"[type=submit], button:not([type=button]), [type=image]"
)) {
events.add_event_listener(
button,
"click",
"pat-inject--form-submit-click",
(e) => {
// make sure the submitting button is sent
// with the form
ajax.onClickSubmit(e);
}
);
}
} else if ($el.is(".pat-subform")) {
log.debug("Initializing subform with injection");
} else {
Expand Down Expand Up @@ -165,37 +181,40 @@ const inject = {
/* Injection has been triggered, either via form submission or a
* link has been clicked.
*/
const $el = $(e.currentTarget);
const cfgs = $el.data("pat-inject");
if ($el.is("form")) {
$(cfgs).each((i, v) => {
v.params = $.param($el.serializeArray());
});
}

// Prevent the original event from doing it's work.
// We want an AJAX request instead.
e.preventDefault && e.preventDefault();
$el.trigger("patterns-inject-triggered");
this.execute(cfgs, $el);
},

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

const $button = $(e.target);
const formaction = $button.attr("formaction");
const $form = $button.parents(".pat-inject").first();
const opts = {
url: formaction,
};
const $cfg_node = $button.closest("[data-pat-inject]");
const cfgs = this.extractConfig($cfg_node, opts);
const submitter = e.submitter;
const formaction = submitter?.getAttribute("formaction");
if (formaction) {
const opts = {
url: formaction,
};

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

e.preventDefault();
$form.trigger("patterns-inject-triggered");
this.execute(cfgs, $form);
for (const cfg of cfgs) {
cfg.params = $.param($el.serializeArray());
}
}

$el.trigger("patterns-inject-triggered");
this.execute(cfgs, $el);
},

submitSubform($sub) {
Expand Down
Loading

0 comments on commit a0fa4d2

Please sign in to comment.