Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extend AnchorTagHelper with automatic attribute in ASP Net.Core 2.2

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!

like image 732
SZL Avatar asked Oct 17 '25 10:10

SZL


2 Answers

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);
        }
    }
}
like image 63
Shahab Avatar answered Oct 20 '25 01:10

Shahab


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.

like image 28
Chris Pratt Avatar answered Oct 19 '25 23:10

Chris Pratt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!