diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/AnchorTagHelper.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/AnchorTagHelper.cs new file mode 100644 index 0000000000..68c29c27f1 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/AnchorTagHelper.cs @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using Microsoft.AspNet.Mvc.Rendering; +using Microsoft.AspNet.Mvc.TagHelpers.Internal; +using Microsoft.AspNet.Razor.Runtime.TagHelpers; + +namespace Microsoft.AspNet.Mvc.TagHelpers +{ + /// + /// implementation targeting <a> elements. + /// + [TagName("a")] + public class AnchorTagHelper : TagHelper + { + private const string RouteAttributePrefix = "route-"; + + [Activate] + private ViewContext ViewContext { get; set; } + + [Activate] + private IHtmlGenerator Generator { get; set; } + + /// + /// The name of the action method. + /// + /// Cannot be provided if is specified. + public string Action { get; set; } + + /// + /// The name of the controller. + /// + /// Cannot be provided if is specified. + public string Controller { get; set; } + + /// + /// The protocol for the URL, such as "http" or "https". + /// + public string Protocol { get; set; } + + /// + /// The host name for the URL. + /// + public string Host { get; set; } + + /// + /// The URL fragment name (the anchor name). + /// + public string Fragment { get; set; } + + /// + /// Name of the route. + /// + /// Cannot be provided if or is specified. + public string Route { get; set; } + + /// + /// Does nothing if user provides a "href" attribute. Cannot specify a "href" attribute AND + /// , or . + public override void Process(TagHelperContext context, TagHelperOutput output) + { + // If there's an "href" on the tag it means it's being used as a normal anchor. + if (output.Attributes.ContainsKey("href")) + { + if (Action != null || Controller != null || Route != null) + { + // User specified an href AND a Action, Controller or Route; can't determine the href attribute. + throw new InvalidOperationException( + Resources.FormatAnchorTagHelper_CannotDetermineHrefOneSpecified( + nameof(AnchorTagHelper), + nameof(Action), + nameof(Controller), + nameof(Route))); + } + + // User is using the AnchorTagHelper as normal anchor tag, restore any provided attributes. + RestoreBoundHtmlAttributes(context, output); + } + else + { + TagBuilder tagBuilder; + + var prefixedValues = TagHelperOutputHelper.PullPrefixedAttributes(RouteAttributePrefix, output); + + // Generator.GenerateActionLink || GenerateRouteLink does not accept a Dictionary for + // route values. + var routeValues = prefixedValues.ToDictionary( + attribute => attribute.Key.Substring(RouteAttributePrefix.Length), + attribute => (object)attribute.Value); + + if (Route == null) + { + tagBuilder = Generator.GenerateActionLink(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( + nameof(AnchorTagHelper), + nameof(Route), + nameof(Action), + nameof(Controller))); + } + else + { + tagBuilder = Generator.GenerateRouteLink(linkText: string.Empty, + routeName: Route, + protocol: Protocol, + hostName: Host, + fragment: Fragment, + routeValues: routeValues, + htmlAttributes: null); + } + + TagHelperOutputHelper.MergeAttributes(tagBuilder, output); + } + } + + private void RestoreBoundHtmlAttributes(TagHelperContext context, TagHelperOutput output) + { + if (Action != null) + { + TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Action), context, output); + } + + if (Controller != null) + { + TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Controller), context, output); + } + + if (Protocol != null) + { + TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Protocol), context, output); + } + + if (Host != null) + { + TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Host), context, output); + } + + if (Fragment != null) + { + TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Fragment), context, output); + } + + if (Route != null) + { + TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Route), context, output); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs b/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs index 3497a16522..a8561eb3d7 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs @@ -10,6 +10,38 @@ internal static class Resources private static readonly ResourceManager _resourceManager = new ResourceManager("Microsoft.AspNet.Mvc.TagHelpers.Resources", typeof(Resources).GetTypeInfo().Assembly); + /// + /// Cannot determine an href for {0}. {0}s with a specified href must not have a {1}, {2} or {3} attribute. + /// + internal static string AnchorTagHelper_CannotDetermineHrefOneSpecified + { + get { return GetString("AnchorTagHelper_CannotDetermineHrefOneSpecified"); } + } + + /// + /// Cannot determine an href for {0}. {0}s with a specified href must not have a {1}, {2} or {3} attribute. + /// + internal static string FormatAnchorTagHelper_CannotDetermineHrefOneSpecified(object p0, object p1, object p2, object p3) + { + return string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotDetermineHrefOneSpecified"), p0, p1, p2, p3); + } + + /// + /// Cannot determine an href for {0}. {0}s with a specified {1} must not have a {2} or {3} attribute. + /// + internal static string AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified + { + get { return GetString("AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified"); } + } + + /// + /// Cannot determine an href for {0}. {0}s with a specified {1} must not have a {2} or {3} attribute. + /// + internal static string FormatAnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified(object p0, object p1, object p2, object p3) + { + return string.Format(CultureInfo.CurrentCulture, GetString("AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified"), p0, p1, p2, p3); + } + /// /// Cannot determine an {1} for {0}. A {0} with a speicified URL based {1} must not have a {2} attribute. /// diff --git a/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx b/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx index a0b6303df9..8307027f3b 100644 --- a/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx +++ b/src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx @@ -117,6 +117,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Cannot determine an href for {0}. {0}s with a specified href must not have a {1}, {2} or {3} attribute. + + + Cannot determine an href for {0}. {0}s with a specified {1} must not have a {2} or {3} attribute. + Cannot determine an {1} for {0}. A {0} with a speicified URL based {1} must not have a {2} attribute.