Added fragment to FormActionTagHelper and FormActionTagHelper

Addresses #4917
This commit is contained in:
Jaspreet Bagga 2016-10-18 09:46:50 -07:00 committed by GitHub
parent 4e1ec39a1f
commit e7c992ff06
7 changed files with 203 additions and 56 deletions

View File

@ -18,18 +18,21 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlTargetElement("button", Attributes = ActionAttributeName)] [HtmlTargetElement("button", Attributes = ActionAttributeName)]
[HtmlTargetElement("button", Attributes = ControllerAttributeName)] [HtmlTargetElement("button", Attributes = ControllerAttributeName)]
[HtmlTargetElement("button", Attributes = AreaAttributeName)] [HtmlTargetElement("button", Attributes = AreaAttributeName)]
[HtmlTargetElement("button", Attributes = FragmentAttributeName)]
[HtmlTargetElement("button", Attributes = RouteAttributeName)] [HtmlTargetElement("button", Attributes = RouteAttributeName)]
[HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)] [HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)]
[HtmlTargetElement("button", Attributes = RouteValuesPrefix + "*")] [HtmlTargetElement("button", Attributes = RouteValuesPrefix + "*")]
[HtmlTargetElement("input", Attributes = ImageActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageFragmentAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageRouteValuesSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = ImageRouteValuesSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitFragmentAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitRouteValuesSelector, TagStructure = TagStructure.WithoutEndTag)] [HtmlTargetElement("input", Attributes = SubmitRouteValuesSelector, TagStructure = TagStructure.WithoutEndTag)]
@ -38,6 +41,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
private const string ActionAttributeName = "asp-action"; private const string ActionAttributeName = "asp-action";
private const string AreaAttributeName = "asp-area"; private const string AreaAttributeName = "asp-area";
private const string ControllerAttributeName = "asp-controller"; private const string ControllerAttributeName = "asp-controller";
private const string FragmentAttributeName = "asp-fragment";
private const string RouteAttributeName = "asp-route"; private const string RouteAttributeName = "asp-route";
private const string RouteValuesDictionaryName = "asp-all-route-data"; private const string RouteValuesDictionaryName = "asp-all-route-data";
private const string RouteValuesPrefix = "asp-route-"; private const string RouteValuesPrefix = "asp-route-";
@ -46,6 +50,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
private const string ImageTypeSelector = "[type=image], "; private const string ImageTypeSelector = "[type=image], ";
private const string ImageActionAttributeSelector = ImageTypeSelector + ActionAttributeName; private const string ImageActionAttributeSelector = ImageTypeSelector + ActionAttributeName;
private const string ImageAreaAttributeSelector = ImageTypeSelector + AreaAttributeName; private const string ImageAreaAttributeSelector = ImageTypeSelector + AreaAttributeName;
private const string ImageFragmentAttributeSelector = ImageTypeSelector + FragmentAttributeName;
private const string ImageControllerAttributeSelector = ImageTypeSelector + ControllerAttributeName; private const string ImageControllerAttributeSelector = ImageTypeSelector + ControllerAttributeName;
private const string ImageRouteAttributeSelector = ImageTypeSelector + RouteAttributeName; private const string ImageRouteAttributeSelector = ImageTypeSelector + RouteAttributeName;
private const string ImageRouteValuesDictionarySelector = ImageTypeSelector + RouteValuesDictionaryName; private const string ImageRouteValuesDictionarySelector = ImageTypeSelector + RouteValuesDictionaryName;
@ -54,6 +59,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
private const string SubmitTypeSelector = "[type=submit], "; private const string SubmitTypeSelector = "[type=submit], ";
private const string SubmitActionAttributeSelector = SubmitTypeSelector + ActionAttributeName; private const string SubmitActionAttributeSelector = SubmitTypeSelector + ActionAttributeName;
private const string SubmitAreaAttributeSelector = SubmitTypeSelector + AreaAttributeName; private const string SubmitAreaAttributeSelector = SubmitTypeSelector + AreaAttributeName;
private const string SubmitFragmentAttributeSelector = SubmitTypeSelector + FragmentAttributeName;
private const string SubmitControllerAttributeSelector = SubmitTypeSelector + ControllerAttributeName; private const string SubmitControllerAttributeSelector = SubmitTypeSelector + ControllerAttributeName;
private const string SubmitRouteAttributeSelector = SubmitTypeSelector + RouteAttributeName; private const string SubmitRouteAttributeSelector = SubmitTypeSelector + RouteAttributeName;
private const string SubmitRouteValuesDictionarySelector = SubmitTypeSelector + RouteValuesDictionaryName; private const string SubmitRouteValuesDictionarySelector = SubmitTypeSelector + RouteValuesDictionaryName;
@ -100,6 +106,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlAttributeName(AreaAttributeName)] [HtmlAttributeName(AreaAttributeName)]
public string Area { get; set; } public string Area { get; set; }
/// <summary>
/// Gets or sets the URL fragment.
/// </summary>
[HtmlAttributeName(FragmentAttributeName)]
public string Fragment { get; set; }
/// <summary> /// <summary>
/// Name of the route. /// Name of the route.
/// </summary> /// </summary>
@ -134,7 +146,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
/// <remarks>Does nothing if user provides an <c>formaction</c> attribute.</remarks> /// <remarks>Does nothing if user provides an <c>formaction</c> attribute.</remarks>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// Thrown if <c>formaction</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>, /// Thrown if <c>formaction</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/>,
/// or <see cref="Route"/> are non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes. /// <see cref="Fragment"/> or <see cref="Route"/> are non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes.
/// Also thrown if <see cref="Route"/> and one or both of <see cref="Action"/> and <see cref="Controller"/> /// Also thrown if <see cref="Route"/> and one or both of <see cref="Action"/> and <see cref="Controller"/>
/// are non-<c>null</c> /// are non-<c>null</c>
/// </exception> /// </exception>
@ -156,6 +168,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
if (Action != null || if (Action != null ||
Controller != null || Controller != null ||
Area != null || Area != null ||
Fragment != null ||
Route != null || Route != null ||
(_routeValues != null && _routeValues.Count > 0)) (_routeValues != null && _routeValues.Count > 0))
{ {
@ -167,6 +180,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
ActionAttributeName, ActionAttributeName,
ControllerAttributeName, ControllerAttributeName,
AreaAttributeName, AreaAttributeName,
FragmentAttributeName,
RouteAttributeName, RouteAttributeName,
RouteValuesPrefix, RouteValuesPrefix,
FormAction)); FormAction));
@ -194,7 +208,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
if (Route == null) if (Route == null)
{ {
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext); var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
var url = urlHelper.Action(Action, Controller, routeValues); var url = urlHelper.Action(Action, Controller, routeValues, protocol: null, host: null, fragment: Fragment);
output.Attributes.SetAttribute(FormAction, url); output.Attributes.SetAttribute(FormAction, url);
} }
else if (Action != null || Controller != null) else if (Action != null || Controller != null)
@ -206,12 +220,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
RouteAttributeName, RouteAttributeName,
ActionAttributeName, ActionAttributeName,
ControllerAttributeName, ControllerAttributeName,
FormAction)); FormAction,
FragmentAttributeName));
} }
else else
{ {
var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext); var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
var url = urlHelper.RouteUrl(Route, routeValues); var url = urlHelper.RouteUrl(Route, routeValues, protocol: null, host: null, fragment: Fragment);
output.Attributes.SetAttribute(FormAction, url); output.Attributes.SetAttribute(FormAction, url);
} }
} }

View File

@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlTargetElement("form", Attributes = ActionAttributeName)] [HtmlTargetElement("form", Attributes = ActionAttributeName)]
[HtmlTargetElement("form", Attributes = AntiforgeryAttributeName)] [HtmlTargetElement("form", Attributes = AntiforgeryAttributeName)]
[HtmlTargetElement("form", Attributes = AreaAttributeName)] [HtmlTargetElement("form", Attributes = AreaAttributeName)]
[HtmlTargetElement("form", Attributes = FragmentAttributeName)]
[HtmlTargetElement("form", Attributes = ControllerAttributeName)] [HtmlTargetElement("form", Attributes = ControllerAttributeName)]
[HtmlTargetElement("form", Attributes = RouteAttributeName)] [HtmlTargetElement("form", Attributes = RouteAttributeName)]
[HtmlTargetElement("form", Attributes = RouteValuesDictionaryName)] [HtmlTargetElement("form", Attributes = RouteValuesDictionaryName)]
@ -25,6 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
private const string ActionAttributeName = "asp-action"; private const string ActionAttributeName = "asp-action";
private const string AntiforgeryAttributeName = "asp-antiforgery"; private const string AntiforgeryAttributeName = "asp-antiforgery";
private const string AreaAttributeName = "asp-area"; private const string AreaAttributeName = "asp-area";
private const string FragmentAttributeName = "asp-fragment";
private const string ControllerAttributeName = "asp-controller"; private const string ControllerAttributeName = "asp-controller";
private const string RouteAttributeName = "asp-route"; private const string RouteAttributeName = "asp-route";
private const string RouteValuesDictionaryName = "asp-all-route-data"; private const string RouteValuesDictionaryName = "asp-all-route-data";
@ -78,6 +80,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlAttributeName(AntiforgeryAttributeName)] [HtmlAttributeName(AntiforgeryAttributeName)]
public bool? Antiforgery { get; set; } public bool? Antiforgery { get; set; }
/// <summary>
/// Gets or sets the URL fragment.
/// </summary>
[HtmlAttributeName(FragmentAttributeName)]
public string Fragment { get; set; }
/// <summary> /// <summary>
/// Name of the route. /// Name of the route.
/// </summary> /// </summary>
@ -121,7 +129,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
/// <c>false</c>. /// <c>false</c>.
/// </remarks> /// </remarks>
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// Thrown if <c>action</c> attribute is provided and <see cref="Action"/> or <see cref="Controller"/> are /// Thrown if <c>action</c> attribute is provided and <see cref="Action"/>, <see cref="Controller"/> or <see cref="Fragment"/> are
/// non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes. /// non-<c>null</c> or if the user provided <c>asp-route-*</c> attributes.
/// </exception> /// </exception>
public override void Process(TagHelperContext context, TagHelperOutput output) public override void Process(TagHelperContext context, TagHelperOutput output)
@ -148,6 +156,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
if (Action != null || if (Action != null ||
Controller != null || Controller != null ||
Area != null || Area != null ||
Fragment != null ||
Route != null || Route != null ||
(_routeValues != null && _routeValues.Count > 0)) (_routeValues != null && _routeValues.Count > 0))
{ {
@ -158,6 +167,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
HtmlActionAttributeName, HtmlActionAttributeName,
ActionAttributeName, ActionAttributeName,
ControllerAttributeName, ControllerAttributeName,
FragmentAttributeName,
AreaAttributeName, AreaAttributeName,
RouteAttributeName, RouteAttributeName,
RouteValuesPrefix)); RouteValuesPrefix));
@ -198,6 +208,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
ViewContext, ViewContext,
Action, Action,
Controller, Controller,
Fragment,
routeValues, routeValues,
method: null, method: null,
htmlAttributes: null); htmlAttributes: null);
@ -211,7 +222,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
RouteAttributeName, RouteAttributeName,
ActionAttributeName, ActionAttributeName,
ControllerAttributeName, ControllerAttributeName,
HtmlActionAttributeName)); HtmlActionAttributeName,
FragmentAttributeName));
} }
else else
{ {
@ -219,6 +231,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
ViewContext, ViewContext,
Route, Route,
routeValues, routeValues,
Fragment,
method: null, method: null,
htmlAttributes: null); htmlAttributes: null);
} }

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
} }
/// <summary> /// <summary>
/// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{6}' or an '{2}' or '{3}' or '{4}' or '{5}' attribute. /// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{7}' or an '{2}', '{3}', '{4}', '{5}', or '{6}' attribute.
/// </summary> /// </summary>
internal static string FormTagHelper_CannotOverrideAction internal static string FormTagHelper_CannotOverrideAction
{ {
@ -51,11 +51,11 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
} }
/// <summary> /// <summary>
/// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{6}' or an '{2}' or '{3}' or '{4}' or '{5}' attribute. /// Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{7}' or an '{2}', '{3}', '{4}', '{5}', or '{6}' attribute.
/// </summary> /// </summary>
internal static string FormatFormTagHelper_CannotOverrideAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6) internal static string FormatFormTagHelper_CannotOverrideAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7)
{ {
return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotOverrideAction"), p0, p1, p2, p3, p4, p5, p6); return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotOverrideAction"), p0, p1, p2, p3, p4, p5, p6, p7);
} }
/// <summary> /// <summary>
@ -147,11 +147,11 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
} }
/// <summary> /// <summary>
/// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute. /// Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.
/// </summary> /// </summary>
internal static string FormatFormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4) internal static string FormatFormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4, object p5)
{ {
return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified"), p0, p1, p2, p3, p4); return string.Format(CultureInfo.CurrentCulture, GetString("FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified"), p0, p1, p2, p3, p4, p5);
} }
/// <summary> /// <summary>
@ -179,11 +179,11 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
} }
/// <summary> /// <summary>
/// Cannot override the '{6}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute. /// Cannot override the '{6}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{7}' must not have attributes starting with '{6}' or an '{1}', '{2}', '{3}', '{4}', or '{5}' attribute.
/// </summary> /// </summary>
internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6) internal static string FormatFormActionTagHelper_CannotOverrideFormAction(object p0, object p1, object p2, object p3, object p4, object p5, object p6, object p7)
{ {
return string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6); return string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotOverrideFormAction"), p0, p1, p2, p3, p4, p5, p6, p7);
} }
/// <summary> /// <summary>
@ -195,11 +195,11 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
} }
/// <summary> /// <summary>
/// Cannot determine a '{4}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{1}' must not have an '{2}' or '{3}' attribute. /// Cannot determine a '{4}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.
/// </summary> /// </summary>
internal static string FormatFormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4) internal static string FormatFormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3, object p4, object p5)
{ {
return string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified"), p0, p1, p2, p3, p4); return string.Format(CultureInfo.CurrentCulture, GetString("FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified"), p0, p1, p2, p3, p4, p5);
} }
private static string GetString(string name, params string[] formatterNames) private static string GetString(string name, params string[] formatterNames)

View File

@ -124,7 +124,7 @@
<value>Cannot override the '{9}' attribute for {0}. An {0} with a specified '{9}' must not have attributes starting with '{8}' or an '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', or '{7}' attribute.</value> <value>Cannot override the '{9}' attribute for {0}. An {0} with a specified '{9}' must not have attributes starting with '{8}' or an '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', or '{7}' attribute.</value>
</data> </data>
<data name="FormTagHelper_CannotOverrideAction" xml:space="preserve"> <data name="FormTagHelper_CannotOverrideAction" xml:space="preserve">
<value>Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{6}' or an '{2}' or '{3}' or '{4}' or '{5}' attribute.</value> <value>Cannot override the '{1}' attribute for {0}. A {0} with a specified '{1}' must not have attributes starting with '{7}' or an '{2}', '{3}', '{4}', '{5}', or '{6}' attribute.</value>
</data> </data>
<data name="InputTagHelper_InvalidExpressionResult" xml:space="preserve"> <data name="InputTagHelper_InvalidExpressionResult" xml:space="preserve">
<value>Unexpected '{1}' expression result type '{2}' for {0}. '{1}' must be of type '{3}' if '{4}' is '{5}'.</value> <value>Unexpected '{1}' expression result type '{2}' for {0}. '{1}' must be of type '{3}' if '{4}' is '{5}'.</value>
@ -142,15 +142,15 @@
<value>The attribute '{0}' does not exist in the {1}.</value> <value>The attribute '{0}' does not exist in the {1}.</value>
</data> </data>
<data name="FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified" xml:space="preserve"> <data name="FormTagHelper_CannotDetermineActionWithRouteAndActionOrControllerSpecified" xml:space="preserve">
<value>Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}' or '{3}' attribute.</value> <value>Cannot determine an '{4}' attribute for {0}. A {0} with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.</value>
</data> </data>
<data name="PropertyOfTypeCannotBeNull" xml:space="preserve"> <data name="PropertyOfTypeCannotBeNull" xml:space="preserve">
<value>The '{0}' property of '{1}' must not be null.</value> <value>The '{0}' property of '{1}' must not be null.</value>
</data> </data>
<data name="FormActionTagHelper_CannotOverrideFormAction" xml:space="preserve"> <data name="FormActionTagHelper_CannotOverrideFormAction" xml:space="preserve">
<value>Cannot override the '{6}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{6}' must not have attributes starting with '{5}' or an '{1}', '{2}', '{3}', or '{4}' attribute.</value> <value>Cannot override the '{7}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{7}' must not have attributes starting with '{6}' or an '{1}', '{2}', '{3}', '{4}', or '{5}' attribute.</value>
</data> </data>
<data name="FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified" xml:space="preserve"> <data name="FormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified" xml:space="preserve">
<value>Cannot determine a '{4}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{1}' must not have an '{2}' or '{3}' attribute.</value> <value>Cannot determine a '{4}' attribute for &lt;{0}&gt;. &lt;{0}&gt; elements with a specified '{1}' must not have an '{2}', '{3}', or '{5}' attribute.</value>
</data> </data>
</root> </root>

View File

@ -0,0 +1,51 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
public static class DefaultHtmlGeneratorExtensions
{
public static TagBuilder GenerateForm(
this IHtmlGenerator generator,
ViewContext viewContext,
string actionName,
string controllerName,
string fragment,
object routeValues,
string method,
object htmlAttributes)
{
var tagBuilder = generator.GenerateForm(viewContext, actionName, controllerName, routeValues, method, htmlAttributes);
// Append the fragment to action
if (fragment != null)
{
tagBuilder.Attributes["action"] += "#" + fragment;
}
return tagBuilder;
}
public static TagBuilder GenerateRouteForm(
this IHtmlGenerator generator,
ViewContext viewContext,
string routeName,
object routeValues,
string fragment,
string method,
object htmlAttributes)
{
var tagBuilder = generator.GenerateRouteForm(viewContext, routeName, routeValues, method, htmlAttributes);
// Append the fragment to action
if (fragment != null)
{
tagBuilder.Attributes["action"] += "#" + fragment;
}
return tagBuilder;
}
}
}

View File

@ -221,7 +221,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{ {
Assert.Equal("delete", param.Action, StringComparer.Ordinal); Assert.Equal("delete", param.Action, StringComparer.Ordinal);
Assert.Equal("books", param.Controller, StringComparer.Ordinal); Assert.Equal("books", param.Controller, StringComparer.Ordinal);
Assert.Null(param.Fragment); Assert.Equal("test", param.Fragment, StringComparer.Ordinal);
Assert.Null(param.Host); Assert.Null(param.Host);
Assert.Null(param.Protocol); Assert.Null(param.Protocol);
Assert.Equal<KeyValuePair<string, object>>(expectedRouteValues, param.Values as RouteValueDictionary); Assert.Equal<KeyValuePair<string, object>>(expectedRouteValues, param.Values as RouteValueDictionary);
@ -238,6 +238,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
{ {
Action = "delete", Action = "delete",
Controller = "books", Controller = "books",
Fragment = "test",
RouteValues = routeValues, RouteValues = routeValues,
ViewContext = viewContext, ViewContext = viewContext,
}; };
@ -277,9 +278,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
.Setup(mock => mock.RouteUrl(It.IsAny<UrlRouteContext>())) .Setup(mock => mock.RouteUrl(It.IsAny<UrlRouteContext>()))
.Callback<UrlRouteContext>(param => .Callback<UrlRouteContext>(param =>
{ {
Assert.Null(param.Fragment);
Assert.Null(param.Host); Assert.Null(param.Host);
Assert.Null(param.Protocol); Assert.Null(param.Protocol);
Assert.Equal("test", param.Fragment, StringComparer.Ordinal);
Assert.Equal("Default", param.RouteName, StringComparer.Ordinal); Assert.Equal("Default", param.RouteName, StringComparer.Ordinal);
Assert.Equal<KeyValuePair<string, object>>(expectedRouteValues, param.Values as RouteValueDictionary); Assert.Equal<KeyValuePair<string, object>>(expectedRouteValues, param.Values as RouteValueDictionary);
}) })
@ -294,6 +295,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
var tagHelper = new FormActionTagHelper(urlHelperFactory.Object) var tagHelper = new FormActionTagHelper(urlHelperFactory.Object)
{ {
Route = "Default", Route = "Default",
Fragment = "test",
RouteValues = routeValues, RouteValues = routeValues,
ViewContext = viewContext, ViewContext = viewContext,
}; };
@ -467,7 +469,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
var expectedErrorMessage = $"Cannot override the 'formaction' attribute for <{tagName}>. <{tagName}> " + var expectedErrorMessage = $"Cannot override the 'formaction' attribute for <{tagName}>. <{tagName}> " +
"elements with a specified 'formaction' must not have attributes starting with 'asp-route-' or an " + "elements with a specified 'formaction' must not have attributes starting with 'asp-route-' or an " +
"'asp-action', 'asp-controller', 'asp-area', or 'asp-route' attribute."; "'asp-action', 'asp-controller', 'asp-area', 'asp-fragment', or 'asp-route' attribute.";
var context = new TagHelperContext( var context = new TagHelperContext(
allAttributes: new TagHelperAttributeList( allAttributes: new TagHelperAttributeList(
@ -502,7 +504,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
attributes: new TagHelperAttributeList(), attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null)); getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
var expectedErrorMessage = $"Cannot determine a 'formaction' attribute for <{tagName}>. <{tagName}> " + var expectedErrorMessage = $"Cannot determine a 'formaction' attribute for <{tagName}>. <{tagName}> " +
"elements with a specified 'asp-route' must not have an 'asp-action' or 'asp-controller' attribute."; "elements with a specified 'asp-route' must not have an 'asp-action', 'asp-controller', or 'asp-fragment' attribute.";
var context = new TagHelperContext( var context = new TagHelperContext(
allAttributes: new TagHelperAttributeList( allAttributes: new TagHelperAttributeList(

View File

@ -284,6 +284,72 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
Assert.Empty(output.PostContent.GetContent()); Assert.Empty(output.PostContent.GetContent());
} }
[Fact]
public async Task ProcessAsync_AspFragmentAddsFragmentToAction()
{
// Arrange
var expectedTagName = "form";
var metadataProvider = new TestModelMetadataProvider();
var tagHelperContext = new TagHelperContext(
allAttributes: new TagHelperAttributeList
{
{ "id", "myform" },
{ "asp-route-name", "value" },
{ "asp-action", "index" },
{ "asp-controller", "home" },
{ "asp-fragment", "test" },
{ "method", "post" },
{ "asp-antiforgery", true }
},
items: new Dictionary<object, object>(),
uniqueId: "test");
var output = new TagHelperOutput(
expectedTagName,
attributes: new TagHelperAttributeList
{
{ "id", "myform" },
},
getChildContentAsync: (useCachedResult, encoder) =>
{
var tagHelperContent = new DefaultTagHelperContent();
return Task.FromResult<TagHelperContent>(tagHelperContent);
});
var urlHelper = new Mock<IUrlHelper>();
urlHelper
.Setup(mock => mock.Action(It.IsAny<UrlActionContext>())).Returns("home/index");
var htmlGenerator = new TestableHtmlGenerator(metadataProvider, urlHelper.Object);
var viewContext = TestableHtmlGenerator.GetViewContext(
model: null,
htmlGenerator: htmlGenerator,
metadataProvider: metadataProvider);
var formTagHelper = new FormTagHelper(htmlGenerator)
{
Action = "index",
Antiforgery = true,
Controller = "home",
Fragment = "test",
ViewContext = viewContext,
RouteValues =
{
{ "name", "value" },
},
};
// Act
await formTagHelper.ProcessAsync(tagHelperContext, output);
// Assert
Assert.Equal("form", output.TagName);
Assert.Equal(TagMode.StartTagAndEndTag, output.TagMode);
var attribute = Assert.Single(output.Attributes, attr => attr.Name.Equals("action"));
Assert.Equal("home/index#test", attribute.Value);
}
[Fact] [Fact]
public async Task ProcessAsync_AspAreaAddsAreaToRouteValues() public async Task ProcessAsync_AspAreaAddsAreaToRouteValues()
{ {
@ -585,7 +651,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
var expectedErrorMessage = "Cannot override the 'action' attribute for <form>. A <form> with a specified " + var expectedErrorMessage = "Cannot override the 'action' attribute for <form>. A <form> with a specified " +
"'action' must not have attributes starting with 'asp-route-' or an " + "'action' must not have attributes starting with 'asp-route-' or an " +
"'asp-action' or 'asp-controller' or 'asp-area' or 'asp-route' attribute."; "'asp-action', 'asp-controller', 'asp-fragment', 'asp-area', or 'asp-route' attribute.";
var context = new TagHelperContext( var context = new TagHelperContext(
allAttributes: new TagHelperAttributeList( allAttributes: new TagHelperAttributeList(
@ -616,7 +682,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
attributes: new TagHelperAttributeList(), attributes: new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null)); getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(null));
var expectedErrorMessage = "Cannot determine an 'action' attribute for <form>. A <form> with a specified " + var expectedErrorMessage = "Cannot determine an 'action' attribute for <form>. A <form> with a specified " +
"'asp-route' must not have an 'asp-action' or 'asp-controller' attribute."; "'asp-route' must not have an 'asp-action', 'asp-controller', or 'asp-fragment' attribute.";
var context = new TagHelperContext( var context = new TagHelperContext(
allAttributes: new TagHelperAttributeList( allAttributes: new TagHelperAttributeList(