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

Commit

Permalink
Add AnchorTagHelper.
Browse files Browse the repository at this point in the history
- Added a TagHelper that targets the <a> tag and allows users to do the equivalent of Html.ActionLink or Html.RouteLink.

#1247
  • Loading branch information
NTaylorMullen committed Oct 11, 2014
1 parent 8f2cdfb commit ee5f9ca
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 0 deletions.
162 changes: 162 additions & 0 deletions src/Microsoft.AspNet.Mvc.TagHelpers/AnchorTagHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Mvc.TagHelpers.Internal;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;

namespace Microsoft.AspNet.Mvc.TagHelpers
{
/// <summary>
/// <see cref="ITagHelper"/> implementation targeting &lt;a&gt; elements.
/// </summary>
[TagName("a")]
public class AnchorTagHelper : TagHelper
{
private const string RouteAttributePrefix = "route-";

[Activate]
private ViewContext ViewContext { get; set; }

[Activate]
private IHtmlGenerator Generator { get; set; }

/// <summary>
/// The name of the action method.
/// </summary>
/// <remarks>Cannot be provided if <see cref="Route"/> is specified.</remarks>
public string Action { get; set; }

/// <summary>
/// The name of the controller.
/// </summary>
/// <remarks>Cannot be provided if <see cref="Route"/> is specified.</remarks>
public string Controller { get; set; }

/// <summary>
/// The protocol for the URL, such as &quot;http&quot; or &quot;https&quot;.
/// </summary>
public string Protocol { get; set; }

/// <summary>
/// The host name for the URL.
/// </summary>
public string Host { get; set; }

/// <summary>
/// The URL fragment name (the anchor name).
/// </summary>
public string Fragment { get; set; }

/// <summary>
/// Name of the route.
/// </summary>
/// <remarks>Cannot be provided if <see cref="Action"/> or <see cref="Controller"/> is specified.</remarks>
public string Route { get; set; }

/// <inheritdoc />
/// <remarks>Does nothing if user provides a "href" attribute. Cannot specify a "href" attribute AND
/// <see cref="Action"/>, <see cref="Controller"/> or <see cref="Route"/>.</remarks>
public override void Process(TagHelperContext context, TagHelperOutput output)
{
// If there's an "href" on the tag it means it's being used as a normal anchor.
if (output.Attributes.ContainsKey("href"))
{
if (Action != null || Controller != null || Route != null)
{
// User specified an href AND a Action, Controller or Route; can't determine the href attribute.
throw new InvalidOperationException(
Resources.FormatAnchorTagHelper_CannotDetermineHrefOneSpecified(
nameof(AnchorTagHelper),
nameof(Action),
nameof(Controller),
nameof(Route)));
}

// User is using the AnchorTagHelper as normal anchor tag, restore any provided attributes.
RestoreBoundHtmlAttributes(context, output);
}
else
{
TagBuilder tagBuilder;

var prefixedValues = TagHelperOutputHelper.PullPrefixedAttributes(RouteAttributePrefix, output);

// Generator.GenerateActionLink || GenerateRouteLink does not accept a Dictionary<string, string> for
// route values.
var routeValues = prefixedValues.ToDictionary(
attribute => attribute.Key.Substring(RouteAttributePrefix.Length),
attribute => (object)attribute.Value);

if (Route == null)
{
tagBuilder = Generator.GenerateActionLink(linkText: string.Empty,
actionName: Action,
controllerName: Controller,
protocol: Protocol,
hostname: Host,
fragment: Fragment,
routeValues: routeValues,
htmlAttributes: null);
}
else if (Action != null || Controller != null)
{
// Route and Action or Controller were specified. Can't determine the href attribute.
throw new InvalidOperationException(
Resources.FormatAnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified(
nameof(AnchorTagHelper),
nameof(Route),
nameof(Action),
nameof(Controller)));
}
else
{
tagBuilder = Generator.GenerateRouteLink(linkText: string.Empty,
routeName: Route,
protocol: Protocol,
hostName: Host,
fragment: Fragment,
routeValues: routeValues,
htmlAttributes: null);
}

TagHelperOutputHelper.MergeAttributes(tagBuilder, output);
}
}

private void RestoreBoundHtmlAttributes(TagHelperContext context, TagHelperOutput output)
{
if (Action != null)
{
TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Action), context, output);
}

if (Controller != null)
{
TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Controller), context, output);
}

if (Protocol != null)
{
TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Protocol), context, output);
}

if (Host != null)
{
TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Host), context, output);
}

if (Fragment != null)
{
TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Fragment), context, output);
}

if (Route != null)
{
TagHelperOutputHelper.RestoreBoundHtmlAttribute(nameof(Route), context, output);
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/Microsoft.AspNet.Mvc.TagHelpers/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="AnchorTagHelper_CannotDetermineHrefOneSpecified" xml:space="preserve">
<value>Cannot determine an href for {0}. {0}s with a specified href must not have a {1}, {2} or {3} attribute.</value>
</data>
<data name="AnchorTagHelper_CannotDetermineHrefRouteActionOrControllerSpecified" xml:space="preserve">
<value>Cannot determine an href for {0}. {0}s with a specified {1} must not have a {2} or {3} attribute.</value>
</data>
<data name="FormTagHelper_CannotDetermineAction" xml:space="preserve">
<value>Cannot determine an {1} for {0}. {0}s with a url based {1} must not have a {2} attribute.</value>
</data>
Expand Down

0 comments on commit ee5f9ca

Please sign in to comment.