I try extend the built-in AnchorTagHelper object to add culture related settings to the generated url automatically.
My code:
namespace MyCoreSite.Mvc.TagHelpers
{
[HtmlTargetElement("a")]
public class MyAnchorTagHelper : AnchorTagHelper
{
public MyAnchorTagHelper(IHtmlGenerator generator) : base(generator)
{
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
var requestCulture = this.ViewContext.HttpContext.Features.Get<IRequestCultureFeature>();
if (context.AllAttributes["asp-route-culture"] == null)
{
this.RouteValues.Add("culture", requestCulture.RequestCulture.UICulture.Name);
}
base.Process(context, output);
output.TagName = "a";
}
}
}
The problem is, when I use the default 'a' tag name in [HtmlTargetElement("a")] attribute it throws this exception:
System.InvalidOperationException: 'Cannot override the 'href' attribute for . An with a specified 'href' must not have attributes starting with 'asp-route-' or an 'asp-action', 'asp-controller', 'asp-area', 'asp-route', 'asp-protocol', 'asp-host', 'asp-fragment', 'asp-page' or 'asp-page-handler' attribute.'
If I use another tag name instead of 'a' (for example 'aa') its working, but I would like use 'a' tag in my views.
Please help me fix this problem! Thank you!
You can inherit from default AnchorTagHelper
, override the Process
method and then remove the original Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper
in _ViewImports.cshtml
file. Using this method you will only have one AnchorTagHelper
in your page.
Example _ViewImports.cshtml
:
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@removeTagHelper Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper *, YourProject.PlatformExtensions
Example inherited AnchorTagHelper
:
namespace YourProject.PlatformExtensions.TagHelpers
{
public static class AnchorTagHelperSettings
{
public static string DefaultCulture = "en";
}
[HtmlTargetElement("a", Attributes = ActionAttributeName)]
[HtmlTargetElement("a", Attributes = ControllerAttributeName)]
[HtmlTargetElement("a", Attributes = AreaAttributeName)]
[HtmlTargetElement("a", Attributes = PageAttributeName)]
[HtmlTargetElement("a", Attributes = PageHandlerAttributeName)]
[HtmlTargetElement("a", Attributes = FragmentAttributeName)]
[HtmlTargetElement("a", Attributes = HostAttributeName)]
[HtmlTargetElement("a", Attributes = ProtocolAttributeName)]
[HtmlTargetElement("a", Attributes = RouteAttributeName)]
[HtmlTargetElement("a", Attributes = RouteValuesDictionaryName)]
[HtmlTargetElement("a", Attributes = RouteValuesPrefix + "*")]
public class AnchorTagHelper : Microsoft.AspNetCore.Mvc.TagHelpers.AnchorTagHelper
{
private const string ActionAttributeName = "asp-action";
private const string ControllerAttributeName = "asp-controller";
private const string AreaAttributeName = "asp-area";
private const string PageAttributeName = "asp-page";
private const string PageHandlerAttributeName = "asp-page-handler";
private const string FragmentAttributeName = "asp-fragment";
private const string HostAttributeName = "asp-host";
private const string ProtocolAttributeName = "asp-protocol";
private const string RouteAttributeName = "asp-route";
private const string RouteValuesDictionaryName = "asp-all-route-data";
private const string RouteValuesPrefix = "asp-route-";
private const string Href = "href"; public override int Order => base.Order;
/// <summary>
/// Creates a new <see cref="AnchorTagHelper"/>.
/// </summary>
/// <param name="generator">The <see cref="IHtmlGenerator"/>.</param>
public AnchorTagHelper(IHtmlGenerator generator) : base(generator)
{
}
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (!output.Attributes.ContainsName(Href) &&
!context.AllAttributes.ContainsName("asp-route-lang") &&
!(RouteValues?.ContainsKey("lang") == true))
{
var routeValues = ViewContext?.RouteData?.Values;
if (routeValues != null)
{
var langValue = routeValues["lang"]?.ToString();
if (string.IsNullOrWhiteSpace(langValue))
{
langValue = AnchorTagHelperSettings.DefaultCulture;
}
if (RouteValues == null)
{
RouteValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
RouteValues.Add("lang", langValue);
}
}
base.Process(context, output);
}
}
}
Each tag helper runs independently in order, so you can't really override a default tag helper like AnchorTagHelper
unless you remove the tag helper library entirely (remove the line that brings it in in _ViewImports.cshtml
). Otherwise, what's actually happening here is that the default AchorTagHelper
processes the tag, and then your custom MyAnchorTagHelper
is processing the already modified tag. Inheriting from built-in tag helpers can actually cause problems if they're depending on a particular attribute being present before that isn't after.
For your particular scenario here, your best bet is to not inherit from AnchorTagHelper
and instead, just modify the href
attribute that will have been generated from AnchorTagHelper
processing the tag first. Just make sure your tag helper library is included after the default in _ViewImports.cshtml
, since order of ops will matter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With