Feature Request: Extend Anchor Tag Helper's asp-route-{value} and asp-all-route-data properties to (properly) render parameters with multiple values.
Currently, ASP.Net Core MVC translates multiple values for a single query string parameter into an array. However, reversing the process鈥攖ranslating an array into a query string containing multiple values for single argument鈥攄oesn't seem to be supported by Anchor Tag Helper.
If http://localhost:5000/SomeController/SomeAction?id=A&id=B is executed against the following action method:
public IActionResult SomeAction(string[] id) { /* ... */ }
Function argument id will be set to the equivalent of new[] { "A", "B" }.
However, try using Anchor Tag Helper to create the above link by using an id parameter set to new[] { "A", "B" }:
<a [email protected]>
In the link that's output, the query string's id parameter is the equivalent of doing .ToString() on the array then escaping it:
?id=System.String%5B%5D
The request is to change this behavior so that the query string is output as:
?id=A&id=B
and to make the equivalent change to asp-all-route-data so that it supports similar functionality.
Thanks for contacting us, @bgribaudo.
@dougbu, what can we do here?
First off, a workaround / recommendation: Don't use the short form for the query string, instead use
<a asp-route-hello[0]="23" asp-route-hello[1]="24"></a>
Is it required to use the short form for your scenario?
what can we do here?
Main thing we could do is introduce a new AnchorTagHelper property of type IDictionary<string, object> or RouteValueDictionary. (The first could support similar {prefix}-* attributes to what we have today with asp-route-{key}. The second might require a bit less code.) Later decisions would be mostly low level e.g. about choosing the prefix and deciding how to handle conflicts like asp-all-route-data="dictionary1" asp-all-some-new-prefix="dictionary2".
Unfortunately, there's a lot pushing back against making other changes in the near term e.g.
RouteValues property (IDictionary<string, string>) without introducing a breaking changeIUrlHelper methods eventually called don't support currently passing a literal query string in, making <a asp-query-string="id=A&id=B"></a> (already ugly) more difficultClearing labels and assignment. @mkArtakMSFT, @danroth27 please decide if this (probably small) enhancement is needed and when.
Also, we can't generate a A HTML tag with @Html.ActionLink with mutiple value because RouteValueDictionary doesn't support several same value (it is a dictionnary).
A other issue:
When a html select with multiple value and submit a form with get method, we obtain:
http://www.domain.com?id=1&id=2
When use method Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery, id value is 1,2.
If you use http://www.domain.com/?id=1,2, model fill id with wrong value.
In summary, we can't use select multiple value:
Check test with multiple value and ParseQuery
TestMultipleValue.zip
@zokiad
RouteValueDictionarydoesn't support several same value
The key doesn't need to be repeated because RouteValueDictionary supports e.g. rvd["key"] = new[] { "value1", "value2" }
The Values in the dictionary ParseQuery returns are StringValues. In this case, id is the equivalent of new[] { "1", "2" }. Just happens to ToString() as "1,2".
use http://www.domain.com/?id=1,2, model fill id with wrong value.
Use either ?id=1&id=2 or ?id[0]=1&id[1]=2
Oh yes!
Thus a workaround is to use @Html.ActionLink with RouteValueDictionnary.
But it is more clear the anchor taghelper. Perhaps, asp-all-route-data should accept RouteValueDictionnary?
Thank for our help.
Thanks for the help here, @dougbu.
While the suggestion to support extra arguments for the AnchorTagHelper are valid, that is not a priority for us at the moment.
Hence closing this issue as the main question got answered. May in the future we notice high community demand for this feature, we may reconsider it.
Thank you all for the fast responses!
First off, a workaround / recommendation: Don't use the short form for the query string, instead use
<a asp-route-hello[0]="23" asp-route-hello[1]="24"></a>
@dougbu, I didn't know that was possible. Thanks for the tip!
Is it required to use the short form for your scenario?
I'm working with a site available in multiple languages. The language used when a view is rendered is specified using a route segment (e.g. {lang}/{controller}/{action}).
In Shared/_Layout.cshtml, a set of links are rendered allowing the user to change the language used to render the current page.
Anchor Tag Helper makes rendering these links simple, as long as the query string is not involved. The below renders a link for the current controller and action with the lang route component overridden to "es":
<a asp-route-lang="es">Spanish</a>
However, getting the above to preserve the query string is a bit more complex. I was trying something along the lines of passing ViewContext.Request.Query (which is an IQueryCollection) into asp-all-route-data鈥攕omething like:
<a [email protected] asp-route-lang="es">Spanish</a>
Of course, as is noted earlier in this issue, this doesn't work.
I guess I'll need to take ViewContext.Request.Query, transform any values that are lists into individual key[0]...key[n]s then try passing that into all-route-data....
I case anyone didnt read the response over in details, this works:
C#
Dictionary<string, string> routeData = new Dictionary<string, string>
{
{ "id[0]", "32" },
{ "id[1]", "1123" },
};
HTML
<a asp-page="/Some/Path" asp-all-route-data="routeData">SomeLinkText</a>
currently using [0] it would generate something like that:
Dictionary<string, string> routeData = new Dictionary<string, string>
{
{ "id[0]", "32" },
{ "id[1]", "1123" },
};
Uri generated: `/?id[0]=32&id[1]=1123 which is wrong. I want to have ?id=32&id=1123
@schmitch ?id[0]=32&id[1]=1123 is not "wrong". Model binding will handle that identically to ?id=32&id=1123.
custom model binders won't. so saying "Model binding will handle that identically to " is wrong.
@schmitch we're straying from the original issue into something new. In particular, it's unclear why you're using a custom model binder that sounds less functional than the built-in ones.
We're very likely to lose track of your bug/feedback/question unless you: