Skip to content

Commit

Permalink
hx-error-swap and hx-error-target attributes #1619
Browse files Browse the repository at this point in the history
  • Loading branch information
Telroshan committed Jul 21, 2023
1 parent 91d0630 commit 980eace
Show file tree
Hide file tree
Showing 10 changed files with 875 additions and 20 deletions.
2 changes: 1 addition & 1 deletion src/htmx.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function ajax(verb: string, path: string, selector: string): void;
export function ajax(
verb: string,
path: string,
context: Partial<{ source: any; event: any; handler: any; target: any; swap: any; values: any; headers: any }>
context: Partial<{ source: any; event: any; handler: any; target: any; errorTarget:any; swap: any; errorSwap: any; values: any; headers: any }>
): void;

/**
Expand Down
117 changes: 98 additions & 19 deletions src/htmx.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ return (function () {
getCacheBusterParam: false,
globalViewTransitions: false,
methodsThatUseUrlParams: ["get"],
defaultErrorSwapStyle: "none",
defaultErrorTarget: "mirror",
},
parseInterval:parseInterval,
_:internalEval,
Expand Down Expand Up @@ -714,6 +716,44 @@ return (function () {
}
}

/**
* @param elt
* @param initialTarget
* @returns {{shouldSwap: boolean, target: (HTMLElement|null), targetStr: string}}
*/
function getErrorTargetSpec(elt, initialTarget) {
var targetStr = getClosestAttributeValue(elt, "hx-error-target") || htmx.config.defaultErrorTarget
var swapStr = getClosestAttributeValue(elt, "hx-error-swap") || htmx.config.defaultErrorSwapStyle
if (!swapStr || swapStr === "none") {
return {
shouldSwap: false,
target: null,
targetStr: "",
}
}
if (targetStr) {
var target
if (targetStr === "mirror") {
target = initialTarget
} else if (targetStr === "this") {
target = findThisElement(elt, "hx-error-target") || elt;
} else {
target = querySelectorExt(elt, targetStr)
}
return {
shouldSwap: true,
target: target,
targetStr: targetStr,
}
}
// fallback to normal hx-target if no error target was specified
return {
shouldSwap: true,
target: initialTarget,
targetStr: "",
}
}

function shouldSettleAttribute(name) {
var attributesToSettle = htmx.config.attributesToSettle;
for (var i = 0; i < attributesToSettle.length; i++) {
Expand Down Expand Up @@ -789,7 +829,7 @@ return (function () {

target = beforeSwapDetails.target; // allow re-targeting
if (beforeSwapDetails['shouldSwap']){
swap(swapStyle, target, target, fragment, settleInfo);
swap(swapStyle, target, target, fragment, settleInfo, false);
}
forEach(settleInfo.elts, function (elt) {
triggerEvent(elt, 'htmx:oobAfterSwap', beforeSwapDetails);
Expand Down Expand Up @@ -1025,7 +1065,7 @@ return (function () {
return fragment;
}

function swap(swapStyle, elt, target, fragment, settleInfo) {
function swap(swapStyle, elt, target, fragment, settleInfo, isStandardError) {
switch (swapStyle) {
case "none":
return;
Expand Down Expand Up @@ -1072,7 +1112,11 @@ return (function () {
if (swapStyle === "innerHTML") {
swapInnerHTML(target, fragment, settleInfo);
} else {
swap(htmx.config.defaultSwapStyle, elt, target, fragment, settleInfo);
var defaultSwapStyle = htmx.config.defaultSwapStyle
if (isStandardError && htmx.config.defaultErrorSwapStyle !== "mirror") {
defaultSwapStyle = htmx.config.defaultErrorSwapStyle
}
swap(defaultSwapStyle, elt, target, fragment, settleInfo, isStandardError);
}
}
}
Expand All @@ -1088,14 +1132,14 @@ return (function () {
}
}

function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride) {
function selectAndSwap(swapStyle, target, elt, responseText, settleInfo, selectOverride, isStandardError) {
settleInfo.title = findTitle(responseText);
var fragment = makeFragment(responseText);
if (fragment) {
handleOutOfBandSwaps(elt, fragment, settleInfo);
fragment = maybeSelectFromResponse(elt, fragment, selectOverride);
handlePreservedElements(fragment);
return swap(swapStyle, elt, target, fragment, settleInfo);
return swap(swapStyle, elt, target, fragment, settleInfo, isStandardError);
}
}

Expand Down Expand Up @@ -1697,7 +1741,7 @@ return (function () {
var target = getTarget(elt)
var settleInfo = makeSettleInfo(elt);

selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo)
selectAndSwap(swapSpec.swapStyle, target, elt, response, settleInfo, false)
settleImmediately(settleInfo.tasks)
triggerEvent(elt, "htmx:sseMessage", event)
};
Expand Down Expand Up @@ -2559,13 +2603,28 @@ return (function () {
/**
*
* @param {HTMLElement} elt
* @param {string} swapInfoOverride
* @param {string?} swapInfoOverride
* @param {boolean?} isStandardErrorSwap
* @returns {import("./htmx").HtmxSwapSpecification}
*/
function getSwapSpecification(elt, swapInfoOverride) {
var swapInfo = swapInfoOverride ? swapInfoOverride : getClosestAttributeValue(elt, "hx-swap");
function getSwapSpecification(elt, swapInfoOverride, isStandardErrorSwap) {
var swapInfo
var defaultSwapStyle = htmx.config.defaultSwapStyle
if (swapInfoOverride) {
swapInfo = swapInfoOverride
} else if (isStandardErrorSwap) {
if (htmx.config.defaultErrorSwapStyle !== "mirror") {
defaultSwapStyle = htmx.config.defaultErrorSwapStyle
}
swapInfo = getClosestAttributeValue(elt, "hx-error-swap")
if ((swapInfo && swapInfo === "mirror") || (!swapInfo && htmx.config.defaultErrorSwapStyle === "mirror")) {
swapInfo = getClosestAttributeValue(elt, "hx-swap") || htmx.config.defaultSwapStyle
}
} else {
swapInfo = getClosestAttributeValue(elt, "hx-swap")
}
var swapSpec = {
"swapStyle" : getInternalData(elt).boosted ? 'innerHTML' : htmx.config.defaultSwapStyle,
"swapStyle" : getInternalData(elt).boosted ? 'innerHTML' : defaultSwapStyle,
"swapDelay" : htmx.config.defaultSwapDelay,
"settleDelay" : htmx.config.defaultSettleDelay
}
Expand Down Expand Up @@ -2808,7 +2867,9 @@ return (function () {
headers : context.headers,
values : context.values,
targetOverride: resolveTarget(context.target),
errorTargetOverride: resolveTarget(context.errorTarget),
swapOverride: context.swap,
errorSwapOverride: context.errorSwap,
returnPromise: true
});
}
Expand Down Expand Up @@ -3278,19 +3339,37 @@ return (function () {
}
}

// by default htmx only swaps on 200 return codes and does not swap
// on 204 'No Content'
// this can be overridden by responding to the htmx:beforeSwap event and
// overriding the detail.shouldSwap property
var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204;

var isError = xhr.status >= 400;
// User could force shouldSwap to true from a htmx:beforeSwap listener, in which case we don't want to
// interfere with hx-error-target & hx-error-swap logic by relying solely on isError
var isStandardErrorSwap = false
if (isError) {
var errorSwapSpec = getErrorTargetSpec(elt, target);
if (errorSwapSpec.shouldSwap) {
shouldSwap = true;
isStandardErrorSwap = true
responseInfo.target = errorSwapSpec.target;
if (responseInfo.target == null || responseInfo.target == DUMMY_ELT) {
triggerErrorEvent(elt, 'htmx:targetError', {target: errorSwapSpec.targetStr});
return;
}
}
}

if (hasHeader(xhr,/HX-Retarget:/i)) {
responseInfo.target = getDocument().querySelector(xhr.getResponseHeader("HX-Retarget"));
}

var historyUpdate = determineHistoryUpdates(elt, responseInfo);

// by default htmx only swaps on 200 return codes and does not swap
// on 204 'No Content'
// this can be ovverriden by responding to the htmx:beforeSwap event and
// overriding the detail.shouldSwap property
var shouldSwap = xhr.status >= 200 && xhr.status < 400 && xhr.status !== 204;
var serverResponse = xhr.response;
var isError = xhr.status >= 400;

var beforeSwapDetails = mergeObjects({shouldSwap: shouldSwap, serverResponse:serverResponse, isError:isError}, responseInfo);
if (!triggerEvent(target, 'htmx:beforeSwap', beforeSwapDetails)) return;

Expand All @@ -3316,11 +3395,11 @@ return (function () {
saveCurrentPageToHistory();
}

var swapOverride = etc.swapOverride;
var swapOverride = isError ? etc.errorSwapOverride : etc.swapOverride;
if (hasHeader(xhr,/HX-Reswap:/i)) {
swapOverride = xhr.getResponseHeader("HX-Reswap");
}
var swapSpec = getSwapSpecification(elt, swapOverride);
var swapSpec = getSwapSpecification(elt, swapOverride, isStandardErrorSwap);

target.classList.add(htmx.config.swappingClass);

Expand Down Expand Up @@ -3350,7 +3429,7 @@ return (function () {
}

var settleInfo = makeSettleInfo(target);
selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride);
selectAndSwap(swapSpec.swapStyle, target, elt, serverResponse, settleInfo, selectOverride, isStandardErrorSwap);

if (selectionInfo.elt &&
!bodyContains(selectionInfo.elt) &&
Expand Down
Loading

0 comments on commit 980eace

Please sign in to comment.