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