htmx
htmx copied to clipboard
hx-boost & support for form* (formaction, formmethod etc) attributes
Potentially again a very esoteric use case, but would be nice if htmx natively supported submit button form overriding attributes: formaction, formmethod. At least these I think are handled by htmx on regular forms, the other formenctype, formnovalidate, formtarget I don't think are handled or even need to be (other than maybe enctype multipart/form-data).
In my case this is needed for bulk action use cases. E.g. we have a data table, which allows selecting rows & then doing different bulk actions on the selected rows (e.g. bulk delete, bulk activate etc). Something like this:
<form>
<button type="submit" formaction="BulkMethod1">Do bulk action 1</button>
<button type="submit" formaction="BulkMethod2">Do bulk action 2</button>
<table>
<thead>
<tr>
<th><input type="checkbox" title="Select/Deselect all"/></th>
...
</tr>
</thead>
<tbody>
<tr>
<td><input type="checkbox" name="ids" value="Id1" /></td>
...
</tr>
<tr>
<td><input type="checkbox" name="ids" value="Id2" /></td>
...
</tr>
</tbody>
</table>
</form>
At least for now it is possible to manually handle this case:
htmx.on('htmx:configRequest', function (e) {
const element = e.detail.elt;
if (element && element.nodeName === 'FORM') {
let submitter = e.detail.triggeringEvent.submitter;
if (submitter) {
// Element also has "formAction" field, but if "formaction" attribute is not filled then it has a default value.
// So we specifically need to check for the existence of the attribute
const form_action = submitter.attributes['formaction'];
if (form_action && form_action.value) {
e.detail.path = form_action;
}
const form_method = submitter.attributes['formmethod']
if (form_method && form_method.value) {
e.detail.verb = form_method.value.toLowerCase();
}
}
}
});
This looks pretty cool. Since you're hooking into htmx:configRequest, you could probably wrap this up as an extension that could be activated/deactivated as needed. I think that's the current direction that the project is taking -- using extensions as something of a playground to experiment with new features, then roll the most ubiquitous ones into the core library. Care to write up docs and a PR?
The suggested workaround is not working for me in the case of formnovalidate because configRequest happens after the validation fails.
As a first time HTMX user, I am currently primarily relying on hx-boost=true and the lack of support for formaction and formmethod created some confusion. It actually makes things non-functional which isn't great. The code snippet above works like a charm though, so thanks for sharing that!
Actually, it doesn't work. e.detail.triggeringEvent.submitter is not defined for me.
The submitter event exists on the FormSubmit event if I add an event handler myself. It seems htmx is not passing on all attributes of the event to configRequest?
I found the bug and submitted a patch here: https://github.com/bigskysoftware/htmx/pull/1156
I ran into this today and had to make a small tweak to the workaround above which might be useful to others; when setting the verb we also need to set useUrlParams so that htmx knows whether to send the form values by body or query string:
...
if (form_method && form_method.value) {
e.detail.verb = form_method.value.toLowerCase();
e.detail.useUrlParams = htmx.config.methodsThatUseUrlParams.indexOf(e.detail.verb) >= 0;
}
...
Another thing that needs to be dealt with is setting the correct content-type.
if (formMethod === 'post') {
event.detail.headers['Content-Type'] = 'application/x-www-form-urlencoded';
}
Should probably note that my original workaround was made for htmx version 1.7.0 (and inside an asp.net core 6 app) and at least there it doesn't seem that the above two additions are needed.
This can probably be closed.
This can probably be closed.
Thanks for landing support for this in main library!