This repository has been archived by the owner on Dec 14, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Added a TagHelper that targets the <a> tag and allows users to do the equivalent of Html.ActionLink or Html.RouteLink. - Added tests to validate AnchorTagHelper functionality. #1247
- Loading branch information
1 parent
4c98c8f
commit 57d1c54
Showing
4 changed files
with
429 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
// 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.Collections.Generic; | ||
using System.Linq; | ||
using Microsoft.AspNet.Mvc.Rendering; | ||
using Microsoft.AspNet.Razor.Runtime.TagHelpers; | ||
|
||
namespace Microsoft.AspNet.Mvc.TagHelpers | ||
{ | ||
/// <summary> | ||
/// <see cref="ITagHelper"/> implementation targeting <a> elements. | ||
/// </summary> | ||
[TagName("a")] | ||
public class AnchorTagHelper : TagHelper | ||
{ | ||
private const string RouteAttributePrefix = "route-"; | ||
private const string Href = "href"; | ||
|
||
[Activate] | ||
private IHtmlGenerator Generator { get; set; } | ||
|
||
/// <summary> | ||
/// The name of the action method. | ||
/// </summary> | ||
/// <remarks>Must be <c>null</c> if <see cref="Route"/> is non-<c>null</c>.</remarks> | ||
public string Action { get; set; } | ||
|
||
/// <summary> | ||
/// The name of the controller. | ||
/// </summary> | ||
/// <remarks>Must be <c>null</c> if <see cref="Route"/> is non-<c>null</c>.</remarks> | ||
public string Controller { get; set; } | ||
|
||
/// <summary> | ||
/// The protocol for the URL, such as "http" or "https". | ||
/// </summary> | ||
public string Protocol { get; set; } | ||
|
||
/// <summary> | ||
/// The host name. | ||
/// </summary> | ||
public string Host { get; set; } | ||
|
||
/// <summary> | ||
/// The URL fragment name. | ||
/// </summary> | ||
public string Fragment { get; set; } | ||
|
||
/// <summary> | ||
/// Name of the route. | ||
/// </summary> | ||
/// <remarks> | ||
/// Must be <c>null</c> if <see cref="Action"/> or <see cref="Controller"/> is non-<c>null</c>. | ||
/// </remarks> | ||
public string Route { get; set; } | ||
|
||
/// <inheritdoc /> | ||
/// <remarks>Does nothing if user provides an "href" attribute. Throws an | ||
/// <see cref="InvalidOperationException"/> if "href" attribute is provided and <see cref="Action"/>, | ||
/// <see cref="Controller"/>, or <see cref="Route"/> are non-<c>null</c>.</remarks> | ||
public override void Process(TagHelperContext context, TagHelperOutput output) | ||
{ | ||
var routePrefixedAttributes = output.FindPrefixedAttributes(RouteAttributePrefix); | ||
|
||
// 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 || | ||
Protocol != null || | ||
Host != null || | ||
Fragment != null || | ||
routePrefixedAttributes.Any()) | ||
{ | ||
// User specified an href and one of the bound attributes; can't determine the href attribute. | ||
throw new InvalidOperationException( | ||
Resources.FormatAnchorTagHelper_CannotOverrideSpecifiedHref( | ||
"<a>", | ||
nameof(Action).ToLowerInvariant(), | ||
nameof(Controller).ToLowerInvariant(), | ||
nameof(Route).ToLowerInvariant(), | ||
nameof(Protocol).ToLowerInvariant(), | ||
nameof(Host).ToLowerInvariant(), | ||
nameof(Fragment).ToLowerInvariant(), | ||
RouteAttributePrefix, | ||
Href)); | ||
} | ||
} | ||
else | ||
{ | ||
TagBuilder tagBuilder; | ||
var routeValues = GetRouteValues(output, routePrefixedAttributes); | ||
|
||
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( | ||
"<a>", | ||
nameof(Route).ToLowerInvariant(), | ||
nameof(Action).ToLowerInvariant(), | ||
nameof(Controller).ToLowerInvariant(), | ||
Href)); | ||
} | ||
else | ||
{ | ||
tagBuilder = Generator.GenerateRouteLink(linkText: string.Empty, | ||
routeName: Route, | ||
protocol: Protocol, | ||
hostName: Host, | ||
fragment: Fragment, | ||
routeValues: routeValues, | ||
htmlAttributes: null); | ||
} | ||
|
||
if (tagBuilder != null) | ||
{ | ||
output.MergeAttributes(tagBuilder); | ||
} | ||
} | ||
} | ||
|
||
// TODO: We will not need this method once https://github.com/aspnet/Razor/issues/89 is completed. | ||
private static Dictionary<string, object> GetRouteValues( | ||
TagHelperOutput output, IEnumerable<KeyValuePair<string, string>> routePrefixedAttributes) | ||
{ | ||
Dictionary<string, object> routeValues = null; | ||
if (routePrefixedAttributes.Any()) | ||
{ | ||
// Prefixed values should be treated as bound attributes, remove them from the output. | ||
output.RemoveRange(routePrefixedAttributes); | ||
|
||
// Generator.GenerateForm does not accept a Dictionary<string, string> for route values. | ||
routeValues = routePrefixedAttributes.ToDictionary( | ||
attribute => attribute.Key.Substring(RouteAttributePrefix.Length), | ||
attribute => (object)attribute.Value); | ||
} | ||
|
||
return routeValues; | ||
} | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.