Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Commit 0129fc4

Browse files
author
NTaylorMullen
committed
Add AnchorTagHelper.
- Added a TagHelper that targets the <a> tag and allows users to do the equivalent of Html.ActionLink or Html.RouteLink. #1247
1 parent 011447d commit 0129fc4

File tree

3 files changed

+206
-0
lines changed

3 files changed

+206
-0
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using Microsoft.AspNet.Mvc.Rendering;
8+
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
9+
10+
namespace Microsoft.AspNet.Mvc.TagHelpers
11+
{
12+
/// <summary>
13+
/// <see cref="ITagHelper"/> implementation targeting &lt;a&gt; elements.
14+
/// </summary>
15+
[TagName("a")]
16+
public class AnchorTagHelper : TagHelper
17+
{
18+
private const string RouteAttributePrefix = "route-";
19+
private const string Href = "href";
20+
21+
[Activate]
22+
private IHtmlGenerator Generator { get; set; }
23+
24+
/// <summary>
25+
/// The name of the action method.
26+
/// </summary>
27+
/// <remarks>Cannot be provided if <see cref="Route"/> is specified.</remarks>
28+
public string Action { get; set; }
29+
30+
/// <summary>
31+
/// The name of the controller.
32+
/// </summary>
33+
/// <remarks>Cannot be provided if <see cref="Route"/> is specified.</remarks>
34+
public string Controller { get; set; }
35+
36+
/// <summary>
37+
/// The protocol for the URL, such as &quot;http&quot; or &quot;https&quot;.
38+
/// </summary>
39+
public string Protocol { get; set; }
40+
41+
/// <summary>
42+
/// The host name for the URL.
43+
/// </summary>
44+
public string Host { get; set; }
45+
46+
/// <summary>
47+
/// The URL fragment name (the anchor name).
48+
/// </summary>
49+
public string Fragment { get; set; }
50+
51+
/// <summary>
52+
/// Name of the route.
53+
/// </summary>
54+
/// <remarks>Cannot be provided if <see cref="Action"/> or <see cref="Controller"/> is specified.</remarks>
55+
public string Route { get; set; }
56+
57+
/// <inheritdoc />
58+
/// <remarks>Does nothing if user provides a "href" attribute. Cannot specify a "href" attribute AND
59+
/// <see cref="Action"/>, <see cref="Controller"/> or <see cref="Route"/>.</remarks>
60+
public override void Process(TagHelperContext context, TagHelperOutput output)
61+
{
62+
// If there's an "href" on the tag it means it's being used as a normal anchor.
63+
if (output.Attributes.ContainsKey(Href))
64+
{
65+
if (Action != null || Controller != null || Route != null)
66+
{
67+
// User specified an href AND a Action, Controller or Route; can't determine the href attribute.
68+
throw new InvalidOperationException(
69+
Resources.FormatAnchorTagHelper_CannotDetermineHrefOneSpecified(
70+
nameof(AnchorTagHelper),
71+
nameof(Action),
72+
nameof(Controller),
73+
nameof(Route),
74+
Href));
75+
}
76+
77+
// User is using the AnchorTagHelper as normal anchor tag, restore any provided attributes.
78+
RestoreBoundHtmlAttributes(context, output);
79+
}
80+
else
81+
{
82+
TagBuilder tagBuilder;
83+
84+
var prefixedValues = output.PullPrefixedAttributes(RouteAttributePrefix);
85+
86+
Dictionary<string, object> routeValues = null;
87+
88+
if (prefixedValues.Any())
89+
{
90+
// Generator.GenerateActionLink || GenerateRouteLink does not accept a Dictionary<string, string> for
91+
// route values.
92+
routeValues = prefixedValues.ToDictionary(
93+
attribute => attribute.Key.Substring(RouteAttributePrefix.Length),
94+
attribute => (object)attribute.Value);
95+
}
96+
97+
if (Route == null)
98+
{
99+
tagBuilder = Generator.GenerateActionLink(linkText: string.Empty,
100+
actionName: Action,
101+
controllerName: Controller,
102+
protocol: Protocol,
103+
hostname: Host,
104+
fragment: Fragment,
105+
routeValues: routeValues,
106+
htmlAttributes: null);
107+
}
108+
else if (Action != null || Controller != null)
109+
{
110+
// Route and Action or Controller were specified. Can't determine the href attribute.
111+
throw new InvalidOperationException(
112+
Resources.FormatAnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified(
113+
nameof(AnchorTagHelper),
114+
nameof(Route),
115+
nameof(Action),
116+
nameof(Controller),
117+
Href));
118+
}
119+
else
120+
{
121+
tagBuilder = Generator.GenerateRouteLink(linkText: string.Empty,
122+
routeName: Route,
123+
protocol: Protocol,
124+
hostName: Host,
125+
fragment: Fragment,
126+
routeValues: routeValues,
127+
htmlAttributes: null);
128+
}
129+
130+
output.MergeAttributes(tagBuilder);
131+
}
132+
}
133+
134+
// Restores bound HTML attributes when we detect that a user is using the AnchorTagHelper as a normal <a> tag.
135+
private void RestoreBoundHtmlAttributes(TagHelperContext context, TagHelperOutput output)
136+
{
137+
if (Action != null)
138+
{
139+
output.RestoreBoundHtmlAttribute(nameof(Action), context);
140+
}
141+
142+
if (Controller != null)
143+
{
144+
output.RestoreBoundHtmlAttribute(nameof(Controller), context);
145+
}
146+
147+
if (Protocol != null)
148+
{
149+
output.RestoreBoundHtmlAttribute(nameof(Protocol), context);
150+
}
151+
152+
if (Host != null)
153+
{
154+
output.RestoreBoundHtmlAttribute(nameof(Host), context);
155+
}
156+
157+
if (Fragment != null)
158+
{
159+
output.RestoreBoundHtmlAttribute(nameof(Fragment), context);
160+
}
161+
162+
if (Route != null)
163+
{
164+
output.RestoreBoundHtmlAttribute(nameof(Route), context);
165+
}
166+
}
167+
}
168+
}

src/Microsoft.AspNet.Mvc.TagHelpers/Properties/Resources.Designer.cs

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@
117117
<resheader name="writer">
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120+
<data name="AnchorTagHelper_CannotDetermineHrefOneSpecified" xml:space="preserve">
121+
<value>Cannot determine an {4} for {0}. A {0} with a specified {4] must not have a {1}, {2} or {3} attribute.</value>
122+
</data>
123+
<data name="AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified" xml:space="preserve">
124+
<value>Cannot determine an {4} for {0}. A {0} with a specified {1} must not have a {2} or {3} attribute.</value>
125+
</data>
120126
<data name="FormTagHelper_CannotDetermineAction" xml:space="preserve">
121127
<value>Cannot determine an {1} for {0}. A {0} with a URL-based {1} must not have attributes starting with {3} or a {2} attribute.</value>
122128
</data>

0 commit comments

Comments
 (0)