With MVC5 (and older MVC frameworks), I was able to write my form without specifying the controller/action method:
@using (Html.BeginForm())
{
}
This way, I was able to reuse the form with different URL. If the form was called from the route "/books/2/edit", the HTML generated would be:
<form action="/books/2/edit"></form>
And if I call the form using the URL "/books/add", the HTML generated would be:
<form action="/books/add"></form>
How can I do the same with the tag helper syntax? I have tried all kind of syntaxes but it always generate an empty action attribute:
<form></form>
<form asp-route=""></form>
<form asp-controller="" asp-action=""></form>
Result:
<form></form>
<form action></form>
<form action></form>
When using HTML helpers, values that are not supplied explicitly default to the route values that are in the current request. That is the reason why you can specify BeginForm
with no parameters.
When using tag helpers, this default logic no longer applies - the values must be provided explicitly. There are no defaults.
form
tagThe simplest way to mimic what the HTML helper does with a form
tag is:
<form action="@Url.RouteUrl(this.ViewContext.RouteData.Values)" method="post">
</form>
Html.BeginForm
Note that your current syntax is also still valid in ASP.NET Core MVC:
@using (Html.BeginForm())
{
}
But since you had to ask this question, I would say that it is absolutely not clear how the URL is being generated when using this syntax, which means you should probably change to using Url.RouteUrl
to make it more readable despite being a bit more to write.
Here is an example of how you could use a tag helper to achieve this, although it is a bit ugly.
There is a form tag helper attribute asp-all-route-values
that allows you to pass all of the route values in a single parameter. However, according to asp-all-route-data
must be IDictionary<string,object>
or RouteValueDictionary
, it is not possible to pass a RouteValueDictionary
to this attribute, you would need to convert it to an IDictionary<string, string>
. One way to do that is to build an extension method to make the conversion.
public static class RouteValueDictionaryExtensions
{
public static IDictionary<string, string> ToTagHelperValues(this RouteValueDictionary routeValueDictionary)
{
var result = new Dictionary<string, string>();
foreach (var kvp in routeValueDictionary)
{
result.Add(kvp.Key, kvp.Value as string);
}
return result;
}
}
You can then use a tag helper to generate the current URL as follows:
<form asp-all-route-data="@this.ViewContext.RouteData.Values.ToTagHelperValues()">
</form>
It is also possible to use a form tag with no action
attribute. If you omit the action
attribute, the default behavior in most (if not all) browsers is to use the current URL.
<form method="post">
</form>
WARNING: It is not standards compliant to use this option and technically the behavior of browsers do not have to default to the expected behavior of using the current URL if it is not supplied.
In the end, which method you use is a matter of preference.