Skip to content

Commit

Permalink
forceUseFormData htmx.ajax parameter #z176
Browse files Browse the repository at this point in the history
  • Loading branch information
Telroshan committed May 21, 2024
1 parent 9355c29 commit 701c4a0
Show file tree
Hide file tree
Showing 9 changed files with 57 additions and 26 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@
## Additions
- `hx-error-target` and `hx-error-swap` attributes to allow swapping server's reponse on error (i.e request with a `status >= 300`). Those attributes behave exactly like, respectively, [hx-target](https://htmx.org/attributes/hx-target/) and [hx-swap](https://htmx.org/attributes/hx-swap/). Using the `"mirror"` value will have them act as their standard counterpart
- [`htmx.ajax()`](https://htmx.org/api/#ajax) supports 2 additional properties, `errorTarget` and `errorSwap` in the `context` argument, to override the error target & error swapping behaviour. Those follow the syntax of, respectively, `target` and `swap` properties. `"mirror"` is also supported there.
- [`htmx.ajax()`](https://htmx.org/api/#ajax) supports 1 additional property, `forceUseFormData` in the `context` argument, to force the request to be a `multipart/form-data` one, without having to set the `Content-Type` header by yourself with its boundary, nor to use `hx-encoding` on the `source` element.
- `htmx.config.defaultErrorTarget` and `htmx.config.defaultErrorSwapStyle` properties to configure error swapping default behavior. `defaultErrorTarget` defaults to `"mirror"` and `defaultErrorSwapStyle` defaults to `"none"`
- `htmx.config.httpErrorCodesToSwap` property to whitelist error codes for which to enable error swapping for. Defaults to an empty array, which means all error codes would be swapped, depending on the error swapping strategy
- `nextElementSibling` and `previousElementSibling` extended selectors that mirror the Element [eponymous properties](https://developer.mozilla.org/en-US/docs/Web/API/Element/nextElementSibling)
## Removals
- Removed attributes settling altogether, as it was messing with my [MutationObserver](https://developer.mozilla.org/en/docs/Web/API/MutationObserver) callbacks to apply style to newly added nodes _(for example a line that should be positioned at current local time with a line of JS in a calendar)_, and I had no use for it anyway
- Removed the `hx-select` feature as I don't use it at all, and don't like it either. Imho it shouldn't be used as it encourages reusing, by laziness, endpoints that send back a huge HTML, instead of making specific endpoints to generate & return only the parts they need, resulting in wasted resources spent generating unused content, and heavier responses that take longer to fetch.
- Removed `hx-on` feature altogether as I don't use it at all, plus it results in additional _(and useless in my case)_ queries against the DOM, and imho encourages [bad practices](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#inline_event_handlers_%E2%80%94_dont_use_these)
- Removed `hx-boost` as I never use it and don't like anyway
- Removed `responseHandling` feature in favor of the error swapping feature mentioned above
## Performance
- Added `readLayout` and `writeLayout` API methods, to execute layout read/write operations while avoiding [layout thrashing](https://developers.google.com/web/fundamentals/performance/rendering/avoid-large-complex-layouts-and-layout-thrashing#avoid_layout_thrashing). htmx is also using them internally for its own DOM mutations and accesses. This sytem can be disabled by setting `htmx.config.layoutQueuesEnabled` to `false` _(defaults to `true`)_, in which case the callbacks passed to `readLayout` and `writeLayout` are immediately executed
- Added `htmx.config.cleanUpThrottlingEnabled` property that, when set to `true` _(defaults to `false`)_, will make htmx's clean up process run in chunks of `4 ms` of work instead of cleaning up the entire source element's hierarchy in one go. This comes in handy for large hierarchies where htmx could spend hundreds of ms cleaning elements up recursively, freezing the browser's rendering. Remaining work is now postponed to the next animation frame when spending more than `4 ms` computing
Expand Down
14 changes: 9 additions & 5 deletions dist/htmx.amd.js
Original file line number Diff line number Diff line change
Expand Up @@ -3608,9 +3608,10 @@ var htmx = (function() {
* @param {XMLHttpRequest} xhr
* @param {Element} elt
* @param {FormData} filteredParameters
* @param {boolean} forceUseFormData
* @returns {*|string|null}
*/
function encodeParamsForBody(xhr, elt, filteredParameters) {
function encodeParamsForBody(xhr, elt, filteredParameters, forceUseFormData) {
let encodedParameters = null
withExtensions(elt, function(extension) {
if (encodedParameters == null) {
Expand All @@ -3620,7 +3621,7 @@ var htmx = (function() {
if (encodedParameters != null) {
return encodedParameters
} else {
if (usesFormData(elt, filteredParameters)) {
if (forceUseFormData || usesFormData(elt, filteredParameters)) {
// Force conversion to an actual FormData object in case filteredParameters is a formDataProxy
// See https://github.com/bigskysoftware/htmx/issues/2317
return overrideFormData(new FormData(), formDataFromObject(filteredParameters))
Expand Down Expand Up @@ -3841,7 +3842,8 @@ var htmx = (function() {
swapOverride: context.swap,
errorTargetOverride: resolveTarget(context.errorTarget),
errorSwapOverride: context.errorSwap,
returnPromise: true
returnPromise: true,
forceUseFormData: context.forceUseFormData
})
}
} else {
Expand Down Expand Up @@ -4200,7 +4202,7 @@ var htmx = (function() {

let headers = getHeaders(elt, target, promptResponse)

if (verb !== 'get' && !usesFormData(elt, filteredFormData)) {
if (verb !== 'get' && !usesFormData(elt, filteredFormData) && !etc.forceUseFormData) {
headers['Content-Type'] = 'application/x-www-form-urlencoded'
}

Expand Down Expand Up @@ -4397,7 +4399,7 @@ var htmx = (function() {
})
})
triggerEvent(elt, 'htmx:beforeSend', responseInfo)
const params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredFormData)
const params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredFormData, etc.forceUseFormData)
xhr.send(params)
return promise
}
Expand Down Expand Up @@ -5015,6 +5017,7 @@ var htmx = (function() {
* @property {HtmxSwapStyle} [errorSwap]
* @property {Object|FormData} [values]
* @property {Record<string,string>} [headers]
* @property {boolean} [forceUseFormData] Force the request to be a multipart/form-data request
*/

/**
Expand Down Expand Up @@ -5058,6 +5061,7 @@ var htmx = (function() {
* @property {Object|FormData} [values]
* @property {boolean} [credentials]
* @property {number} [timeout]
* @property {boolean} [forceUseFormData] Force the request to be a multipart/form-data request
*/

/**
Expand Down
14 changes: 9 additions & 5 deletions dist/htmx.cjs.js
Original file line number Diff line number Diff line change
Expand Up @@ -3607,9 +3607,10 @@ var htmx = (function() {
* @param {XMLHttpRequest} xhr
* @param {Element} elt
* @param {FormData} filteredParameters
* @param {boolean} forceUseFormData
* @returns {*|string|null}
*/
function encodeParamsForBody(xhr, elt, filteredParameters) {
function encodeParamsForBody(xhr, elt, filteredParameters, forceUseFormData) {
let encodedParameters = null
withExtensions(elt, function(extension) {
if (encodedParameters == null) {
Expand All @@ -3619,7 +3620,7 @@ var htmx = (function() {
if (encodedParameters != null) {
return encodedParameters
} else {
if (usesFormData(elt, filteredParameters)) {
if (forceUseFormData || usesFormData(elt, filteredParameters)) {
// Force conversion to an actual FormData object in case filteredParameters is a formDataProxy
// See https://github.com/bigskysoftware/htmx/issues/2317
return overrideFormData(new FormData(), formDataFromObject(filteredParameters))
Expand Down Expand Up @@ -3840,7 +3841,8 @@ var htmx = (function() {
swapOverride: context.swap,
errorTargetOverride: resolveTarget(context.errorTarget),
errorSwapOverride: context.errorSwap,
returnPromise: true
returnPromise: true,
forceUseFormData: context.forceUseFormData
})
}
} else {
Expand Down Expand Up @@ -4199,7 +4201,7 @@ var htmx = (function() {

let headers = getHeaders(elt, target, promptResponse)

if (verb !== 'get' && !usesFormData(elt, filteredFormData)) {
if (verb !== 'get' && !usesFormData(elt, filteredFormData) && !etc.forceUseFormData) {
headers['Content-Type'] = 'application/x-www-form-urlencoded'
}

Expand Down Expand Up @@ -4396,7 +4398,7 @@ var htmx = (function() {
})
})
triggerEvent(elt, 'htmx:beforeSend', responseInfo)
const params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredFormData)
const params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredFormData, etc.forceUseFormData)
xhr.send(params)
return promise
}
Expand Down Expand Up @@ -5014,6 +5016,7 @@ var htmx = (function() {
* @property {HtmxSwapStyle} [errorSwap]
* @property {Object|FormData} [values]
* @property {Record<string,string>} [headers]
* @property {boolean} [forceUseFormData] Force the request to be a multipart/form-data request
*/

/**
Expand Down Expand Up @@ -5057,6 +5060,7 @@ var htmx = (function() {
* @property {Object|FormData} [values]
* @property {boolean} [credentials]
* @property {number} [timeout]
* @property {boolean} [forceUseFormData] Force the request to be a multipart/form-data request
*/

/**
Expand Down
14 changes: 9 additions & 5 deletions dist/htmx.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -3607,9 +3607,10 @@ var htmx = (function() {
* @param {XMLHttpRequest} xhr
* @param {Element} elt
* @param {FormData} filteredParameters
* @param {boolean} forceUseFormData
* @returns {*|string|null}
*/
function encodeParamsForBody(xhr, elt, filteredParameters) {
function encodeParamsForBody(xhr, elt, filteredParameters, forceUseFormData) {
let encodedParameters = null
withExtensions(elt, function(extension) {
if (encodedParameters == null) {
Expand All @@ -3619,7 +3620,7 @@ var htmx = (function() {
if (encodedParameters != null) {
return encodedParameters
} else {
if (usesFormData(elt, filteredParameters)) {
if (forceUseFormData || usesFormData(elt, filteredParameters)) {
// Force conversion to an actual FormData object in case filteredParameters is a formDataProxy
// See https://github.com/bigskysoftware/htmx/issues/2317
return overrideFormData(new FormData(), formDataFromObject(filteredParameters))
Expand Down Expand Up @@ -3840,7 +3841,8 @@ var htmx = (function() {
swapOverride: context.swap,
errorTargetOverride: resolveTarget(context.errorTarget),
errorSwapOverride: context.errorSwap,
returnPromise: true
returnPromise: true,
forceUseFormData: context.forceUseFormData
})
}
} else {
Expand Down Expand Up @@ -4199,7 +4201,7 @@ var htmx = (function() {

let headers = getHeaders(elt, target, promptResponse)

if (verb !== 'get' && !usesFormData(elt, filteredFormData)) {
if (verb !== 'get' && !usesFormData(elt, filteredFormData) && !etc.forceUseFormData) {
headers['Content-Type'] = 'application/x-www-form-urlencoded'
}

Expand Down Expand Up @@ -4396,7 +4398,7 @@ var htmx = (function() {
})
})
triggerEvent(elt, 'htmx:beforeSend', responseInfo)
const params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredFormData)
const params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredFormData, etc.forceUseFormData)
xhr.send(params)
return promise
}
Expand Down Expand Up @@ -5014,6 +5016,7 @@ var htmx = (function() {
* @property {HtmxSwapStyle} [errorSwap]
* @property {Object|FormData} [values]
* @property {Record<string,string>} [headers]
* @property {boolean} [forceUseFormData] Force the request to be a multipart/form-data request
*/

/**
Expand Down Expand Up @@ -5057,6 +5060,7 @@ var htmx = (function() {
* @property {Object|FormData} [values]
* @property {boolean} [credentials]
* @property {number} [timeout]
* @property {boolean} [forceUseFormData] Force the request to be a multipart/form-data request
*/

/**
Expand Down
14 changes: 9 additions & 5 deletions dist/htmx.js
Original file line number Diff line number Diff line change
Expand Up @@ -3607,9 +3607,10 @@ var htmx = (function() {
* @param {XMLHttpRequest} xhr
* @param {Element} elt
* @param {FormData} filteredParameters
* @param {boolean} forceUseFormData
* @returns {*|string|null}
*/
function encodeParamsForBody(xhr, elt, filteredParameters) {
function encodeParamsForBody(xhr, elt, filteredParameters, forceUseFormData) {
let encodedParameters = null
withExtensions(elt, function(extension) {
if (encodedParameters == null) {
Expand All @@ -3619,7 +3620,7 @@ var htmx = (function() {
if (encodedParameters != null) {
return encodedParameters
} else {
if (usesFormData(elt, filteredParameters)) {
if (forceUseFormData || usesFormData(elt, filteredParameters)) {
// Force conversion to an actual FormData object in case filteredParameters is a formDataProxy
// See https://github.com/bigskysoftware/htmx/issues/2317
return overrideFormData(new FormData(), formDataFromObject(filteredParameters))
Expand Down Expand Up @@ -3840,7 +3841,8 @@ var htmx = (function() {
swapOverride: context.swap,
errorTargetOverride: resolveTarget(context.errorTarget),
errorSwapOverride: context.errorSwap,
returnPromise: true
returnPromise: true,
forceUseFormData: context.forceUseFormData
})
}
} else {
Expand Down Expand Up @@ -4199,7 +4201,7 @@ var htmx = (function() {

let headers = getHeaders(elt, target, promptResponse)

if (verb !== 'get' && !usesFormData(elt, filteredFormData)) {
if (verb !== 'get' && !usesFormData(elt, filteredFormData) && !etc.forceUseFormData) {
headers['Content-Type'] = 'application/x-www-form-urlencoded'
}

Expand Down Expand Up @@ -4396,7 +4398,7 @@ var htmx = (function() {
})
})
triggerEvent(elt, 'htmx:beforeSend', responseInfo)
const params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredFormData)
const params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredFormData, etc.forceUseFormData)
xhr.send(params)
return promise
}
Expand Down Expand Up @@ -5014,6 +5016,7 @@ var htmx = (function() {
* @property {HtmxSwapStyle} [errorSwap]
* @property {Object|FormData} [values]
* @property {Record<string,string>} [headers]
* @property {boolean} [forceUseFormData] Force the request to be a multipart/form-data request
*/

/**
Expand Down Expand Up @@ -5057,6 +5060,7 @@ var htmx = (function() {
* @property {Object|FormData} [values]
* @property {boolean} [credentials]
* @property {number} [timeout]
* @property {boolean} [forceUseFormData] Force the request to be a multipart/form-data request
*/

/**
Expand Down
2 changes: 1 addition & 1 deletion dist/htmx.min.js

Large diffs are not rendered by default.

Binary file modified dist/htmx.min.js.gz
Binary file not shown.
8 changes: 8 additions & 0 deletions src/htmx.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ type HtmxAjaxHelperContext = {
errorSwap?: HtmxSwapStyle;
values?: any | FormData;
headers?: Record<string, string>;
/**
* Force the request to be a multipart/form-data request
*/
forceUseFormData?: boolean;
};
type HtmxRequestConfig = {
useUrlParams: boolean;
Expand Down Expand Up @@ -174,6 +178,10 @@ type HtmxAjaxEtc = {
values?: any | FormData;
credentials?: boolean;
timeout?: number;
/**
* Force the request to be a multipart/form-data request
*/
forceUseFormData?: boolean;
};
type HtmxBeforeSwapDetails = HtmxResponseInfo & {
shouldSwap: boolean;
Expand Down
14 changes: 9 additions & 5 deletions src/htmx.js
Original file line number Diff line number Diff line change
Expand Up @@ -3607,9 +3607,10 @@ var htmx = (function() {
* @param {XMLHttpRequest} xhr
* @param {Element} elt
* @param {FormData} filteredParameters
* @param {boolean} forceUseFormData
* @returns {*|string|null}
*/
function encodeParamsForBody(xhr, elt, filteredParameters) {
function encodeParamsForBody(xhr, elt, filteredParameters, forceUseFormData) {
let encodedParameters = null
withExtensions(elt, function(extension) {
if (encodedParameters == null) {
Expand All @@ -3619,7 +3620,7 @@ var htmx = (function() {
if (encodedParameters != null) {
return encodedParameters
} else {
if (usesFormData(elt, filteredParameters)) {
if (forceUseFormData || usesFormData(elt, filteredParameters)) {
// Force conversion to an actual FormData object in case filteredParameters is a formDataProxy
// See https://github.com/bigskysoftware/htmx/issues/2317
return overrideFormData(new FormData(), formDataFromObject(filteredParameters))
Expand Down Expand Up @@ -3840,7 +3841,8 @@ var htmx = (function() {
swapOverride: context.swap,
errorTargetOverride: resolveTarget(context.errorTarget),
errorSwapOverride: context.errorSwap,
returnPromise: true
returnPromise: true,
forceUseFormData: context.forceUseFormData
})
}
} else {
Expand Down Expand Up @@ -4199,7 +4201,7 @@ var htmx = (function() {

let headers = getHeaders(elt, target, promptResponse)

if (verb !== 'get' && !usesFormData(elt, filteredFormData)) {
if (verb !== 'get' && !usesFormData(elt, filteredFormData) && !etc.forceUseFormData) {
headers['Content-Type'] = 'application/x-www-form-urlencoded'
}

Expand Down Expand Up @@ -4396,7 +4398,7 @@ var htmx = (function() {
})
})
triggerEvent(elt, 'htmx:beforeSend', responseInfo)
const params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredFormData)
const params = useUrlParams ? null : encodeParamsForBody(xhr, elt, filteredFormData, etc.forceUseFormData)
xhr.send(params)
return promise
}
Expand Down Expand Up @@ -5014,6 +5016,7 @@ var htmx = (function() {
* @property {HtmxSwapStyle} [errorSwap]
* @property {Object|FormData} [values]
* @property {Record<string,string>} [headers]
* @property {boolean} [forceUseFormData] Force the request to be a multipart/form-data request
*/

/**
Expand Down Expand Up @@ -5057,6 +5060,7 @@ var htmx = (function() {
* @property {Object|FormData} [values]
* @property {boolean} [credentials]
* @property {number} [timeout]
* @property {boolean} [forceUseFormData] Force the request to be a multipart/form-data request
*/

/**
Expand Down

0 comments on commit 701c4a0

Please sign in to comment.