Search code examples
c#asp.net-coreasp.net-core-mvcasp.net-core-tag-helpers

Bind TagHelper to attribute starting with data-


I am currently developing an ASP.Net Core 3.0 pre-release 8 MVC application. I created a custom Tag Helper which looks something like the following, which does basically the same as the existing asp-append-version attribute but with the data-src attribute instead of src:

[HtmlTargetElement(
    "img",
    Attributes = AppendVersionAttributeName + "," + SrcAttributeName,
    TagStructure = TagStructure.WithoutEndTag)]
public class ImageTagHelper : UrlResolutionTagHelper
{
    private const string AppendVersionAttributeName = "asp-append-version-lazy";
    private const string SrcAttributeName = "data-src";


    [ActivatorUtilitiesConstructor]
    public ImageTagHelper(
        IFileVersionProvider fileVersionProvider,
        HtmlEncoder htmlEncoder,
        IUrlHelperFactory urlHelperFactory)
        : base(urlHelperFactory, htmlEncoder)
    {
        FileVersionProvider = fileVersionProvider;
    }

    public override int Order => -1000; 

    [HtmlAttributeName(SrcAttributeName)]
    public string? Src { get; set; }


    [HtmlAttributeName(AppendVersionAttributeName)]
    public bool AppendVersion { get; set; }

    internal IFileVersionProvider FileVersionProvider { get; private set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (output == null)
        {
            throw new ArgumentNullException(nameof(output));
        }

        output.CopyHtmlAttribute(SrcAttributeName, context);
        ProcessUrlAttribute(SrcAttributeName, output);

        if (AppendVersion)
        {
            EnsureFileVersionProvider();

            Src = output.Attributes[SrcAttributeName].Value as string;

            output.Attributes.SetAttribute(SrcAttributeName, FileVersionProvider.AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, Src));
        }
    }

    private void EnsureFileVersionProvider()
    {
        if (FileVersionProvider == null)
        {
            FileVersionProvider = ViewContext.HttpContext.RequestServices.GetRequiredService<IFileVersionProvider>();
        }
    }
}

Anyway the important part is that the attribute I want is data-src, but as soon as I try to use my Tag Helper in any view it gives me the following error:

Invalid tag helper bound property '..'. Tag helpers cannot bind to HTML attributes with name 'data-src' because the name starts with 'data-'.

The actual question

What is the reason for this, also is there any way to go "around" it?


Solution

  • According to the source, this is the reason:

    data-* attributes are explicitly not implemented by user agents and are not intended for use on the server; therefore it's invalid for TagHelpers to bind to them.

    So the idea is to explicitly prevent data- attributes from being evaluated on the service side, as a means for them to transfer data solely for the purpose of client-side evaluation.

    This restriction cannot be circumvented as it is built into the Razor SDK that compiles Razor views where your tag helper is being used.

    Thinking about it, it does make sense to have this kind of restriction, especially as you are completely free to use whatever attribute you want on tag helpers. And if you want to use data- attributes because you want to expose them after your tag helper has run, then there’s actually nothing stopping you from doing that as you can always add additional attributes as part of your tag helper execution: Including data- attributes.