Open gnat opened 2 years ago
As a side note, the Hotwire implementation of event pausing / interception: https://github.com/hotwired/turbo/pull/306
Someone else stumbling upon on this:
Is the (relatively recent) htmx:confrm
event a satisfying solution to this: https://htmx.org/events/#htmx:confirm
Looking at the code, making all the places where filter expressions are evaluated handle async well would be a little ugly (e.g. polling and load)
I was working on something that also required async event handlers (i.e. i was using htmx:configRequest
to modify the request but it was async).
I came up with my own hack in htmx to support this logic:
function triggerEvent(elt, eventName, detail, continuation /* this is new */) {
elt = resolveTarget(elt);
if (detail == null) {
detail = {};
}
detail["elt"] = elt;
if (continuation) { /* this is new */
var continuationPromises = [];
detail["continuation"] = function (eventCb) {
var resolveContinuation = null;
continuationPromises.push(
new Promise(function (resolve) {
resolveContinuation = resolve;
})
);
eventCb(resolveContinuation);
};
}
var event = makeEvent(eventName, detail);
if (htmx.logger && !ignoreEventForLogging(eventName)) {
htmx.logger(elt, eventName, detail);
}
if (detail.error) {
logError(detail.error);
triggerEvent(elt, "htmx:error", { errorInfo: detail });
}
var eventResult = elt.dispatchEvent(event);
var kebabName = kebabEventName(eventName);
if (eventResult && kebabName !== eventName) {
var kebabedEvent = makeEvent(kebabName, event.detail);
eventResult = eventResult && elt.dispatchEvent(kebabedEvent);
}
withExtensions(elt, function (extension) {
eventResult =
eventResult && extension.onEvent(eventName, event) !== false;
});
if (continuation) { /* this is new */
return Promise.all(continuationPromises).then(function () {
return continuation(eventResult);
});
}
return eventResult;
}
// ...
return triggerEvent(
elt,
"htmx:configRequest",
requestConfig,
afterConfiguration
);
function afterConfiguration(eventResult) {
if (!eventResult) {
maybeCall(resolve);
endRequestLock();
return promise;
}
// ...
and you set up a handler like this
htmx.on("htmx:configRequest", (event) => {
event.detail.continuation((resolve) => {
// do something async
resolve();
});
});
now triggerEvent will allow listeners to register a promise and wont call the continuation untill all listeners are resolved.
Looks like this example shows how to accomplish all these use cases? https://htmx.org/examples/async-auth/
Use case
Hotwire recently added a similar feature. Would make available some intuitive low-code patterns in htmx.
If
hx-trigger="submit[action()]"
supported async calls, we could "pause" the event, do something, then "resume" the event. Real life uses:hx-trigger="submit[recaptcha(target)]"
hx-trigger="submit[auth(target)]"
Suggestion to achieve this: htmx can simply detect if
hx-trigger
is a promise or vanilla function, and handle it accordingly.Benefits
It would totally dunk on our friends at Hotwire / Turbo to allow this. :sweat_smile: And go very well in the Migration Guide
The Hotwire way: https://turbo.hotwired.dev/handbook/drive#pausing-requests
Would simplify and cut down the amount of event listener code drastically in a very htmx-way.
Issues
Currently, you can pass an async function in, but
maybeGenerateConditional()
doesn't understand it: https://github.com/bigskysoftware/htmx/blob/master/src/htmx.js#L1001@1cg said: