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

Commit 31cb253

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 8c441bf commit 31cb253

File tree

3 files changed

+200
-0
lines changed

3 files changed

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

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 href for {0}. {0}s with a specified href 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 href for {0}. {0}s 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}. {0}s with a url based {1} must not have a {2} attribute.</value>
122128
</data>

0 commit comments

Comments
 (0)