diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/AnchorTagHelper.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/AnchorTagHelper.cs
index 8ef8ae97c6..d7de3ea208 100644
--- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/AnchorTagHelper.cs
+++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/AnchorTagHelper.cs
@@ -16,6 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlTargetElement("a", Attributes = ActionAttributeName)]
[HtmlTargetElement("a", Attributes = ControllerAttributeName)]
[HtmlTargetElement("a", Attributes = AreaAttributeName)]
+ [HtmlTargetElement("a", Attributes = PageAttributeName)]
[HtmlTargetElement("a", Attributes = FragmentAttributeName)]
[HtmlTargetElement("a", Attributes = HostAttributeName)]
[HtmlTargetElement("a", Attributes = ProtocolAttributeName)]
@@ -27,6 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
private const string ActionAttributeName = "asp-action";
private const string ControllerAttributeName = "asp-controller";
private const string AreaAttributeName = "asp-area";
+ private const string PageAttributeName = "asp-page";
private const string FragmentAttributeName = "asp-fragment";
private const string HostAttributeName = "asp-host";
private const string ProtocolAttributeName = "asp-protocol";
@@ -59,24 +61,40 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
///
/// The name of the action method.
///
- /// Must be null if is non-null.
+ ///
+ /// Must be null if or is non-null.
+ ///
[HtmlAttributeName(ActionAttributeName)]
public string Action { get; set; }
///
/// The name of the controller.
///
- /// Must be null if is non-null.
+ ///
+ /// Must be null if or is non-null.
+ ///
[HtmlAttributeName(ControllerAttributeName)]
public string Controller { get; set; }
///
/// The name of the area.
///
- /// Must be null if is non-null.
+ ///
+ /// Must be null if or is non-null.
+ ///
[HtmlAttributeName(AreaAttributeName)]
public string Area { get; set; }
+ ///
+ /// The name of the page.
+ ///
+ ///
+ /// Must be null if or ,
+ /// or is non-null.
+ ///
+ [HtmlAttributeName(PageAttributeName)]
+ public string Page { get; set; }
+
///
/// The protocol for the URL, such as "http" or "https".
///
@@ -99,7 +117,8 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
/// Name of the route.
///
///
- /// Must be null if or is non-null.
+ /// Must be null if one of , ,
+ /// or is non-null.
///
[HtmlAttributeName(RouteAttributeName)]
public string Route { get; set; }
@@ -134,12 +153,6 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
///
/// Does nothing if user provides an href attribute.
- ///
- /// Thrown if href attribute is provided and , ,
- /// , , , or are
- /// non-null or if the user provided asp-route-* attributes. Also thrown if
- /// and one or both of and are non-null.
- ///
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (context == null)
@@ -158,6 +171,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
if (Action != null ||
Controller != null ||
Area != null ||
+ Page != null ||
Route != null ||
Protocol != null ||
Host != null ||
@@ -167,7 +181,9 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
// User specified an href and one of the bound attributes; can't determine the href attribute.
throw new InvalidOperationException(
Resources.FormatAnchorTagHelper_CannotOverrideHref(
+ Href,
"",
+ RouteValuesPrefix,
ActionAttributeName,
ControllerAttributeName,
AreaAttributeName,
@@ -175,72 +191,84 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
ProtocolAttributeName,
HostAttributeName,
FragmentAttributeName,
- RouteValuesPrefix,
- Href));
+ PageAttributeName));
}
+
+ return;
+ }
+
+ var routeLink = Route != null;
+ var actionLink = Controller != null || Action != null;
+ var pageLink = Page != null;
+
+ if ((routeLink && actionLink) || (routeLink && pageLink) || (actionLink && pageLink))
+ {
+ var message = string.Join(
+ Environment.NewLine,
+ Resources.FormatCannotDetermineAttributeFor(Href, ""),
+ RouteAttributeName,
+ ControllerAttributeName + ", " + ActionAttributeName,
+ PageAttributeName);
+
+ throw new InvalidOperationException(message);
+ }
+
+ RouteValueDictionary routeValues = null;
+ if (_routeValues != null && _routeValues.Count > 0)
+ {
+ routeValues = new RouteValueDictionary(_routeValues);
+ }
+
+ if (Area != null)
+ {
+ // Unconditionally replace any value from asp-route-area.
+ if (routeValues == null)
+ {
+ routeValues = new RouteValueDictionary();
+ }
+ routeValues["area"] = Area;
+ }
+
+ TagBuilder tagBuilder;
+ if (pageLink)
+ {
+ tagBuilder = Generator.GeneratePageLink(
+ ViewContext,
+ linkText: string.Empty,
+ pageName: Page,
+ protocol: Protocol,
+ hostname: Host,
+ fragment: Fragment,
+ routeValues: routeValues,
+ htmlAttributes: null);
+ }
+ else if (routeLink)
+ {
+ tagBuilder = Generator.GenerateRouteLink(
+ ViewContext,
+ linkText: string.Empty,
+ routeName: Route,
+ protocol: Protocol,
+ hostName: Host,
+ fragment: Fragment,
+ routeValues: routeValues,
+ htmlAttributes: null);
}
else
{
- RouteValueDictionary routeValues = null;
- if (_routeValues != null && _routeValues.Count > 0)
- {
- routeValues = new RouteValueDictionary(_routeValues);
- }
-
- if (Area != null)
- {
- if (routeValues == null)
- {
- routeValues = new RouteValueDictionary();
- }
-
- // Unconditionally replace any value from asp-route-area.
- routeValues["area"] = Area;
- }
-
- TagBuilder tagBuilder;
- if (Route == null)
- {
- tagBuilder = Generator.GenerateActionLink(
- ViewContext,
- linkText: string.Empty,
- actionName: Action,
- controllerName: Controller,
- protocol: Protocol,
- hostname: Host,
- fragment: Fragment,
- routeValues: routeValues,
- htmlAttributes: null);
- }
- else if (Action != null || Controller != null)
- {
- // Route and Action or Controller were specified. Can't determine the href attribute.
- throw new InvalidOperationException(
- Resources.FormatAnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified(
- "",
- RouteAttributeName,
- ActionAttributeName,
- ControllerAttributeName,
- Href));
- }
- else
- {
- tagBuilder = Generator.GenerateRouteLink(
- ViewContext,
- linkText: string.Empty,
- routeName: Route,
- protocol: Protocol,
- hostName: Host,
- fragment: Fragment,
- routeValues: routeValues,
- htmlAttributes: null);
- }
-
- if (tagBuilder != null)
- {
- output.MergeAttributes(tagBuilder);
- }
+ tagBuilder = Generator.GenerateActionLink(
+ ViewContext,
+ linkText: string.Empty,
+ actionName: Action,
+ controllerName: Controller,
+ protocol: Protocol,
+ hostname: Host,
+ fragment: Fragment,
+ routeValues: routeValues,
+ htmlAttributes: null);
}
+
+ output.MergeAttributes(tagBuilder);
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormActionTagHelper.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormActionTagHelper.cs
index 1b7cc1e401..5c2b8201ca 100644
--- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormActionTagHelper.cs
+++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormActionTagHelper.cs
@@ -18,6 +18,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlTargetElement("button", Attributes = ActionAttributeName)]
[HtmlTargetElement("button", Attributes = ControllerAttributeName)]
[HtmlTargetElement("button", Attributes = AreaAttributeName)]
+ [HtmlTargetElement("button", Attributes = PageAttributeName)]
[HtmlTargetElement("button", Attributes = FragmentAttributeName)]
[HtmlTargetElement("button", Attributes = RouteAttributeName)]
[HtmlTargetElement("button", Attributes = RouteValuesDictionaryName)]
@@ -25,6 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlTargetElement("input", Attributes = ImageActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
+ [HtmlTargetElement("input", Attributes = ImagePageAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageFragmentAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = ImageRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)]
@@ -32,6 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlTargetElement("input", Attributes = SubmitActionAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitControllerAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitAreaAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
+ [HtmlTargetElement("input", Attributes = SubmitPageAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitFragmentAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitRouteAttributeSelector, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("input", Attributes = SubmitRouteValuesDictionarySelector, TagStructure = TagStructure.WithoutEndTag)]
@@ -41,6 +44,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
private const string ActionAttributeName = "asp-action";
private const string AreaAttributeName = "asp-area";
private const string ControllerAttributeName = "asp-controller";
+ private const string PageAttributeName = "asp-page";
private const string FragmentAttributeName = "asp-fragment";
private const string RouteAttributeName = "asp-route";
private const string RouteValuesDictionaryName = "asp-all-route-data";
@@ -50,6 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
private const string ImageTypeSelector = "[type=image], ";
private const string ImageActionAttributeSelector = ImageTypeSelector + ActionAttributeName;
private const string ImageAreaAttributeSelector = ImageTypeSelector + AreaAttributeName;
+ private const string ImagePageAttributeSelector = ImageTypeSelector + PageAttributeName;
private const string ImageFragmentAttributeSelector = ImageTypeSelector + FragmentAttributeName;
private const string ImageControllerAttributeSelector = ImageTypeSelector + ControllerAttributeName;
private const string ImageRouteAttributeSelector = ImageTypeSelector + RouteAttributeName;
@@ -59,6 +64,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
private const string SubmitTypeSelector = "[type=submit], ";
private const string SubmitActionAttributeSelector = SubmitTypeSelector + ActionAttributeName;
private const string SubmitAreaAttributeSelector = SubmitTypeSelector + AreaAttributeName;
+ private const string SubmitPageAttributeSelector = SubmitTypeSelector + PageAttributeName;
private const string SubmitFragmentAttributeSelector = SubmitTypeSelector + FragmentAttributeName;
private const string SubmitControllerAttributeSelector = SubmitTypeSelector + ControllerAttributeName;
private const string SubmitRouteAttributeSelector = SubmitTypeSelector + RouteAttributeName;
@@ -106,6 +112,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlAttributeName(AreaAttributeName)]
public string Area { get; set; }
+ ///
+ /// The name of the page.
+ ///
+ [HtmlAttributeName(PageAttributeName)]
+ public string Page { get; set; }
+
///
/// Gets or sets the URL fragment.
///
@@ -185,51 +197,59 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
RouteValuesPrefix,
FormAction));
}
+
+ return;
+ }
+
+ var routeLink = Route != null;
+ var actionLink = Controller != null || Action != null;
+ var pageLink = Page != null;
+
+ if ((routeLink && actionLink) || (routeLink && pageLink) || (actionLink && pageLink))
+ {
+ var message = string.Join(
+ Environment.NewLine,
+ Resources.FormatCannotDetermineAttributeFor(FormAction, '<' + output.TagName + '>'),
+ RouteAttributeName,
+ ControllerAttributeName + ", " + ActionAttributeName,
+ PageAttributeName);
+
+ throw new InvalidOperationException(message);
+ }
+
+ RouteValueDictionary routeValues = null;
+ if (_routeValues != null && _routeValues.Count > 0)
+ {
+ routeValues = new RouteValueDictionary(_routeValues);
+ }
+
+ if (Area != null)
+ {
+ if (routeValues == null)
+ {
+ routeValues = new RouteValueDictionary();
+ }
+
+ // Unconditionally replace any value from asp-route-area.
+ routeValues["area"] = Area;
+ }
+
+ var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
+ string url;
+ if (pageLink)
+ {
+ url = urlHelper.Page(Page, routeValues, protocol: null, host: null, fragment: Fragment);
+ }
+ else if (routeLink)
+ {
+ url = urlHelper.RouteUrl(Route, routeValues, protocol: null, host: null, fragment: Fragment);
}
else
{
- RouteValueDictionary routeValues = null;
- if (_routeValues != null && _routeValues.Count > 0)
- {
- routeValues = new RouteValueDictionary(_routeValues);
- }
-
- if (Area != null)
- {
- if (routeValues == null)
- {
- routeValues = new RouteValueDictionary();
- }
-
- // Unconditionally replace any value from asp-route-area.
- routeValues["area"] = Area;
- }
-
- if (Route == null)
- {
- var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
- var url = urlHelper.Action(Action, Controller, routeValues, protocol: null, host: null, fragment: Fragment);
- output.Attributes.SetAttribute(FormAction, url);
- }
- else if (Action != null || Controller != null)
- {
- // Route and Action or Controller were specified. Can't determine the formaction attribute.
- throw new InvalidOperationException(
- Resources.FormatFormActionTagHelper_CannotDetermineFormActionRouteActionOrControllerSpecified(
- output.TagName,
- RouteAttributeName,
- ActionAttributeName,
- ControllerAttributeName,
- FormAction,
- FragmentAttributeName));
- }
- else
- {
- var urlHelper = UrlHelperFactory.GetUrlHelper(ViewContext);
- var url = urlHelper.RouteUrl(Route, routeValues, protocol: null, host: null, fragment: Fragment);
- output.Attributes.SetAttribute(FormAction, url);
- }
+ url = urlHelper.Action(Action, Controller, routeValues, protocol: null, host: null, fragment: Fragment);
}
+
+ output.Attributes.SetAttribute(FormAction, url);
}
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs b/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs
index 3b00906fcb..02ae810309 100644
--- a/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs
+++ b/src/Microsoft.AspNetCore.Mvc.TagHelpers/FormTagHelper.cs
@@ -7,6 +7,7 @@ using System.ComponentModel;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
+using Microsoft.AspNetCore.Routing;
namespace Microsoft.AspNetCore.Mvc.TagHelpers
{
@@ -16,6 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlTargetElement("form", Attributes = ActionAttributeName)]
[HtmlTargetElement("form", Attributes = AntiforgeryAttributeName)]
[HtmlTargetElement("form", Attributes = AreaAttributeName)]
+ [HtmlTargetElement("form", Attributes = PageAttributeName)]
[HtmlTargetElement("form", Attributes = FragmentAttributeName)]
[HtmlTargetElement("form", Attributes = ControllerAttributeName)]
[HtmlTargetElement("form", Attributes = RouteAttributeName)]
@@ -26,6 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
private const string ActionAttributeName = "asp-action";
private const string AntiforgeryAttributeName = "asp-antiforgery";
private const string AreaAttributeName = "asp-area";
+ private const string PageAttributeName = "asp-page";
private const string FragmentAttributeName = "asp-fragment";
private const string ControllerAttributeName = "asp-controller";
private const string RouteAttributeName = "asp-route";
@@ -72,6 +75,12 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
[HtmlAttributeName(AreaAttributeName)]
public string Area { get; set; }
+ ///
+ /// The name of the page.
+ ///
+ [HtmlAttributeName(PageAttributeName)]
+ public string Page { get; set; }
+
///
/// Whether the antiforgery token should be generated.
///
@@ -156,6 +165,7 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
if (Action != null ||
Controller != null ||
Area != null ||
+ Page != null ||
Fragment != null ||
Route != null ||
(_routeValues != null && _routeValues.Count > 0))
@@ -163,14 +173,15 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
// User also specified bound attributes we cannot use.
throw new InvalidOperationException(
Resources.FormatFormTagHelper_CannotOverrideAction(
- "