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>Must be <c>null</c> if <see cref="Route"/> is non-<c>null</c>.</remarks>
28
+ public string Action { get ; set ; }
29
+
30
+ /// <summary>
31
+ /// The name of the controller.
32
+ /// </summary>
33
+ /// <remarks>Must be <c>null</c> if <see cref="Route"/> is non-<c>null</c>.</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.
43
+ /// </summary>
44
+ public string Host { get ; set ; }
45
+
46
+ /// <summary>
47
+ /// The URL fragment name.
48
+ /// </summary>
49
+ public string Fragment { get ; set ; }
50
+
51
+ /// <summary>
52
+ /// Name of the route.
53
+ /// </summary>
54
+ /// <remarks>
55
+ /// Must be <c>null</c> if <see cref="Action"/> or <see cref="Controller"/> is non-<c>null</c>.
56
+ /// </remarks>
57
+ public string Route { get ; set ; }
58
+
59
+ /// <inheritdoc />
60
+ /// <remarks>Does nothing if user provides an "href" attribute. Throws an
61
+ /// <see cref="InvalidOperationException"/> if "href" attribute is provided and <see cref="Action"/>,
62
+ /// <see cref="Controller"/>, or <see cref="Route"/> are non-<c>null</c>.</remarks>
63
+ public override void Process ( TagHelperContext context , TagHelperOutput output )
64
+ {
65
+ var routePrefixedAttributes = output . FindPrefixedAttributes ( RouteAttributePrefix ) ;
66
+
67
+ // If there's an "href" on the tag it means it's being used as a normal anchor.
68
+ if ( output . Attributes . ContainsKey ( Href ) )
69
+ {
70
+ if ( Action != null ||
71
+ Controller != null ||
72
+ Route != null ||
73
+ Protocol != null ||
74
+ Host != null ||
75
+ Fragment != null ||
76
+ routePrefixedAttributes . Any ( ) )
77
+ {
78
+ // User specified an href and one of the bound attributes; can't determine the href attribute.
79
+ throw new InvalidOperationException (
80
+ Resources . FormatAnchorTagHelper_CannotOverrideSpecifiedHref (
81
+ "<a>" ,
82
+ nameof ( Action ) . ToLowerInvariant ( ) ,
83
+ nameof ( Controller ) . ToLowerInvariant ( ) ,
84
+ nameof ( Route ) . ToLowerInvariant ( ) ,
85
+ nameof ( Protocol ) . ToLowerInvariant ( ) ,
86
+ nameof ( Host ) . ToLowerInvariant ( ) ,
87
+ nameof ( Fragment ) . ToLowerInvariant ( ) ,
88
+ RouteAttributePrefix ,
89
+ Href ) ) ;
90
+ }
91
+ }
92
+ else
93
+ {
94
+ TagBuilder tagBuilder ;
95
+ var routeValues = GetRouteValues ( output , routePrefixedAttributes ) ;
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
+ "<a>" ,
114
+ nameof ( Route ) . ToLowerInvariant ( ) ,
115
+ nameof ( Action ) . ToLowerInvariant ( ) ,
116
+ nameof ( Controller ) . ToLowerInvariant ( ) ,
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
+ if ( tagBuilder != null )
131
+ {
132
+ output . MergeAttributes ( tagBuilder ) ;
133
+ }
134
+ }
135
+ }
136
+
137
+ // TODO: We will not need this method once https://github.com/aspnet/Razor/issues/89 is completed.
138
+ private static Dictionary < string , object > GetRouteValues (
139
+ TagHelperOutput output , IEnumerable < KeyValuePair < string , string > > routePrefixedAttributes )
140
+ {
141
+ Dictionary < string , object > routeValues = null ;
142
+ if ( routePrefixedAttributes . Any ( ) )
143
+ {
144
+ // Prefixed values should be treated as bound attributes, remove them from the output.
145
+ output . RemoveRange ( routePrefixedAttributes ) ;
146
+
147
+ // Generator.GenerateForm does not accept a Dictionary<string, string> for route values.
148
+ routeValues = routePrefixedAttributes . ToDictionary (
149
+ attribute => attribute . Key . Substring ( RouteAttributePrefix . Length ) ,
150
+ attribute => ( object ) attribute . Value ) ;
151
+ }
152
+
153
+ return routeValues ;
154
+ }
155
+ }
156
+ }
0 commit comments