hx-get="" hx-vals="{index: 2}" creates a duplicate entry instead of replacing index
Using htmx 2.0.4.
According to https://htmx.org/attributes/hx-vals/ "Input values with the same name will be overridden by variable declarations."
However when I specify an entry with hx-vals, it duplicates the existing entry, which breaks my backend. This is in an SVG element (rust / maud syntax):
svg {
g {
line
x1=0 y1=0 x2=100 y2=100
hx-get="" // send to current route
hx-vals=(format!(r#"{{"index":{}}}"#, i + 1)) // update index param
hx-swap="innerHTML"
hx-target=(".target")
hx-push-url="true" { }
}
}
- The first request goes to the expected route:
/foo?file=... - The second requests adds the index param as expected:
/foo?file=...&index=1 - The third+ requests fail because they look like this:
/foo?file=...&index=1&index=2
I tried to use hx-params="* -index" which indeed removed index from the request URL. However, this seemed to override hx-vals and the expected index was never added to the request URL.
Is this a bug in the hx-vals implementation, or am I misunderstanding the docs? "Input values with the same name will be overridden by variable declarations."
The issue here is not related to hx-vals but to the combining of url based parameters and additional parameters from your request. I don't think using both hx-get="" and hx-push-url together like this will work well as this will cause it to combine the previous requests parameter list with the new requests parameter list causing duplicates.
I'm guessing you have another parameter file=xxxx which you are trying to preserve across actions so I would instead work to make this file parameter set explicitly some how. You could render your initial html with the dynamic value in your template like hx-get="/foo?file={{file_param}}" so that when rendering the element in the backend it reuses the file parameter from the request in the response. Then any additional parameters will add as you expect.
Another good option is to use a hidden input for file that you can add to a form or use hx-include and then this file parameter will be included in the request url and you can use just hx-get="/foo".
You can also add an event listener or hx-on for htmx:configRequest that can set the parameters as required in a simple block of code or update event.detail.path to remove the problem parameters from your request path.
Thank you for the response!
The issue here is not related to hx-vals but to the combining of url based parameters and additional parameters from your request. I don't think using both hx-get="" and hx-push-url together like this will work well as this will cause it to combine the previous requests parameter list with the new requests parameter list causing duplicates.
Removing hx-push-url does not seem to have any effect though, simply having hx-get & hx-vals is enough to duplicate index on the parameter list:
hx-get="" // send to current route
hx-vals=(format!(r#"{{"index":{}}}"#, i + 1)) // update index param
I guess my confusion stems from the hx-vals docs which claim values with the same name will be replaced.
I'm guessing you have another parameter file=xxxx which you are trying to preserve across actions so I would instead work to make this file parameter set explicitly some how. You could render your initial html with the dynamic value in your template like
hx-get="/foo?file={{file_param}}"so that when rendering the element in the backend it reuses the file parameter from the request in the response.
That is correct, I have (multiple) other params that I wish to maintain. I tried this at first, but this approach doesn't quite fit my use-case, as I don't necessarily know the exact param list at the time of rendering. That's why I was happy to see in the docs that hx-get="" simply sends to the current URL, and hx-vals replaces existing params.
Another good option is to use a hidden input for file that you can add to a form or use hx-include and then this file parameter will be included in the request url and you can use just hx-get="/foo".
You can also add an event listener or hx-on for htmx:configRequest that can set the parameters as required in a simple block of code or update event.detail.path to remove the problem parameters from your request path.
I expect one of those could work, so I'll try that. But it does feel like a bit more busywork than simply using hx-vals, especially since it's part of an SVG that may contain 100s of copies of this snippet.
I guess my main question would be whether the hx-vals implementation differs from the documentation, or I'm just misunderstanding this statement:
# Notes
[...]
- Input values with the same name will be overridden by variable declarations.
There are probably other ways to reproduce the issue without hx-push-url like hx-boost for example or somehow manually typing in a url with a query parameter into the url bar. What is important is that the url you are on has query parameters.
The real issue here is that while input values from the request are indeed overridden by hx-vals, htmx has a limitation that it does not treat the hx-get url query parameters as input parameters. This true for all htmx parameter code and not just hx-vals. These are just treated as part of the URL to fetch and it just appends the input parameters from the request on the end. It does not have any code to parse, process or read back the url parameters values and deduplicate them with the request parameters. Maybe the hx-get documentation could include a warning that using hx-get="" can cause problems as any parameters from the current url can not be overridden easily.
This is why you should try and use the more standard pattern of just requesting the "/foo" url if you can and allowing htmx to build up the query parameters normally. In a more traditional MPA the query parameters from the last full page request would be used in the full page render to either set an input on the page or be added to the url's of links and actions so sometimes we need to implement similar patterns in htmx websites as well.
Understood, and thanks for the detailed under the hood explanation, this makes sense. Should I go ahead and contribute a PR for the docs?
A last question if I may: I see that hx-params="* -index" correctly excludes the index from the request URL. When I combine hx-params="* -index" hx-vals="index=42" is it expected that hx-params overrides and removes the index from hx-vals too?
Apologies if this is straddling the line between support & issue reporting, I am not yet familiar enough to understand what is intended behavior based on the docs.
(I am digging through the code to understand the flow better.)
The documentation for hx-params https://htmx.org/attributes/hx-params/ has all 4 possible formats for this attribute. it can only be *, none, not <param list> or <param list> where param list is a comma separated list of of field names. "* -index" is not a valid option and may exclude all parameters since none of them will match. the * form has to be alone if you want it to just include all input parameters and if you want to exclude index you should use "not index". hx-params can only be used to specify which parameters to filter out or not from the request but I don't think it will help with the parameters already located in the request url