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 <a> 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 "http" or "https".
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
+ }
0 commit comments