标记帮助程序组件

ASP.NET Core 中的标记帮助程序组件

作者:Scott AddieFiyaz Bin Hasan

标记帮助程序组件是可用于有条件地修改或添加服务器端代码中的 HTML 元素的标记帮助程序。 ASP.NET Core 2.0 或更高版本中提供了此功能。

ASP.NET Core 包括两个内置标记帮助程序组件:headbody 它们位于 Microsoft.AspNetCore.Mvc.Razor.TagHelpers 命名空间中,可用于 MVC 和 Razor Pages。 标记帮助程序组件不需要在 _ViewImports.cshtml 中注册应用。

查看或下载示例代码如何下载

用例

标记帮助程序组件的两个常见用例包括:

  1. <link> 注入到 <head> 中。
  2. <script> 注入到 <body> 中。

以下各节介绍了这些用例。

注入到 HTML head 元素中

在 HTML <head> 元素中,通常使用 HTML <link> 元素导入 CSS 文件。 以下代码使用 head 标记帮助程序组件将 <link> 元素注入到 <head> 元素中:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace RazorPagesSample.TagHelpers
{
    public class AddressStyleTagHelperComponent : TagHelperComponent
    {
        private readonly string _style = 
            @"<link rel=""stylesheet"" href=""/css/address.css"" />";

        public override int Order => 1;

        public override Task ProcessAsync(TagHelperContext context,
                                          TagHelperOutput output)
        {
            if (string.Equals(context.TagName, "head", 
                              StringComparison.OrdinalIgnoreCase))
            {
                output.PostContent.AppendHtml(_style);
            }

            return Task.CompletedTask;
        }
    }
}

在上述代码中:

  • AddressStyleTagHelperComponent 可实现 TagHelperComponent 抽象:
    • 允许初始化带有 TagHelperContext 的类。
    • 启用标记帮助程序组件以添加或修改 HTML 元素。
  • Order 属性可定义呈现组件的顺序。 应用中的标记帮助程序组件有多种用法时,Order 是必需的。
  • ProcessAsync 会将执行上下文的 TagName 属性值与 head 进行比较。 如果比较的计算结果为 true,则 _style 字段的内容将注入到 HTML <head> 元素中。

注入到 HTML body 元素中

body 标记帮助程序组件可以将 <script> 元素注入到 <body> 元素中。 以下代码演示了此项技术:

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Razor.TagHelpers;

namespace RazorPagesSample.TagHelpers
{
    public class AddressScriptTagHelperComponent : TagHelperComponent
    {
        public override int Order => 2;
        
        public override async Task ProcessAsync(TagHelperContext context,
                                                TagHelperOutput output)
        {
            if (string.Equals(context.TagName, "body",
                              StringComparison.OrdinalIgnoreCase))
            {
                var script = await File.ReadAllTextAsync(
                    "TagHelpers/Templates/AddressToolTipScript.html");
                output.PostContent.AppendHtml(script);
            }
        }
    }
}

单独的 HTML 文件用于存储 <script> 元素。 HTML 文件使代码更清洁且更易于维护。 上述代码可读取 TagHelpers/Templates/AddressToolTipScript.html 的内容并将其追加到标记帮助程序输出中。 AddressToolTipScript.html 文件包括以下标记:

<script>
$("address[printable]").hover(function() {
    $(this).attr({
        "data-toggle": "tooltip",
        "data-placement": "right",
        "title": "Home of Microsoft!"
    });
});
</script>

上述代码可将启动工具提示小组件绑定到包含 printable 属性的任何 <address> 元素。 当鼠标指针悬停在元素上时,可以显示效果。

注册组件

标记帮助程序组件必须添加到应用的标记帮助程序组件集合。 有以下三种方法可添加到集合:

通过服务容器注册

如果未使用 ITagHelperComponentManager 管理标记帮助程序组件类,则必须向依赖关系注入 (DI) 系统注册。 以下 Startup.ConfigureServices 代码可注册带有瞬态生存期AddressStyleTagHelperComponentAddressScriptTagHelperComponent 类:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddTransient<ITagHelperComponent, 
        AddressScriptTagHelperComponent>();
    services.AddTransient<ITagHelperComponent, 
        AddressStyleTagHelperComponent>();
}

通过 Razor 文件注册

如果未向 DI 注册标记帮助程序组件,则可以从 Razor Pages 页或 MVC 视图注册。 此项技术用于控制注入的标记和 Razor 文件中的组件执行顺序。

ITagHelperComponentManager 用于添加标记帮助程序组件或从应用中删除这些组件。 以下代码演示了使用 AddressTagHelperComponent 的此项技术:

@using RazorPagesSample.TagHelpers;
@using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
@inject ITagHelperComponentManager manager;

@{
    string markup;

    if (Model.IsWeekend)
    {
        markup = "<em class='text-warning'>Office closed today!</em>";
    }
    else
    {
        markup = "<em class='text-info'>Office open today!</em>";
    }

    manager.Components.Add(new AddressTagHelperComponent(markup, 1));
}

在上述代码中:

  • @inject 指令提供了 ITagHelperComponentManager 的实例。 该实例将分配到名为 manager 的变量以访问 Razor 文件中的下游。
  • AddressTagHelperComponent 的实例将添加到应用的标记帮助程序组件集合。

修改 AddressTagHelperComponent 以适应接受 markuporder 参数的构造函数:

private readonly string _markup;

public override int Order { get; }

public AddressTagHelperComponent(string markup = "", int order = 1)
{
    _markup = markup;
    Order = order;
}

提供的 markup 参数用于 ProcessAsync,如下所示:

public override async Task ProcessAsync(TagHelperContext context,
                                        TagHelperOutput output)
{
    if (string.Equals(context.TagName, "address",
            StringComparison.OrdinalIgnoreCase) &&
        output.Attributes.ContainsName("printable"))
    {
        TagHelperContent childContent = await output.GetChildContentAsync();
        string content = childContent.GetContent();
        output.Content.SetHtmlContent(
            $"<div>{content}<br>{_markup}</div>{_printableButton}");
    }
}

通过页面模型或控制器注册

如果未向 DI 注册标记帮助程序组件,则可以从 Razor Pages 页面模型或 MVC 控制器注册。 此项技术可用于将 C# 逻辑与 Razor 文件隔离。

构造函数注入用于访问 ITagHelperComponentManager 的实例。 标记帮助程序组件将添加到该实例的标记帮助程序组件集合。 以下 Razor Pages 页面模型演示了使用 AddressTagHelperComponent 的此项技术:

using System;
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.RazorPages;
using RazorPagesSample.TagHelpers;

public class IndexModel : PageModel
{
    private readonly ITagHelperComponentManager _tagHelperComponentManager;

    public bool IsWeekend
    {
        get
        {
            var dayOfWeek = DateTime.Now.DayOfWeek;

            return dayOfWeek == DayOfWeek.Saturday ||
                   dayOfWeek == DayOfWeek.Sunday;
        }
    }

    public IndexModel(ITagHelperComponentManager tagHelperComponentManager)
    {
        _tagHelperComponentManager = tagHelperComponentManager;
    }

    public void OnGet()
    {
        string markup;

        if (IsWeekend)
        {
            markup = "<em class='text-warning'>Office closed today!</em>";
        }
        else
        {
            markup = "<em class='text-info'>Office open today!</em>";
        }

        _tagHelperComponentManager.Components.Add(
            new AddressTagHelperComponent(markup, 1));
    }
}

在上述代码中:

  • 构造函数注入用于访问 ITagHelperComponentManager 的实例。
  • AddressTagHelperComponent 的实例将添加到应用的标记帮助程序组件集合。

创建组件

创建自定义标记帮助程序组件:

以下代码可创建面向 <address> HTML 元素的自定义标记帮助程序组件:

using System.ComponentModel;
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Logging;

namespace RazorPagesSample.TagHelpers
{
    [HtmlTargetElement("address")]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public class AddressTagHelperComponentTagHelper : TagHelperComponentTagHelper
    {
        public AddressTagHelperComponentTagHelper(
            ITagHelperComponentManager componentManager, 
            ILoggerFactory loggerFactory) : base(componentManager, loggerFactory)
        {
        }
    }
}

按如下所示使用自定义 address 标记帮助程序组件注入 HTML 标记:

public class AddressTagHelperComponent : TagHelperComponent
{
    private readonly string _printableButton =
        "<button type='button' class='btn btn-info' onclick=\"window.open(" +
        "'https://binged.it/2AXRRYw')\">" +
        "<span class='glyphicon glyphicon-road' aria-hidden='true'></span>" +
        "</button>";

    public override int Order => 3;

    public override async Task ProcessAsync(TagHelperContext context,
                                            TagHelperOutput output)
    {
        if (string.Equals(context.TagName, "address",
                StringComparison.OrdinalIgnoreCase) &&
            output.Attributes.ContainsName("printable"))
        {
            var content = await output.GetChildContentAsync();
            output.Content.SetHtmlContent(
                $"<div>{content.GetContent()}</div>{_printableButton}");
        }
    }
}

上述 ProcessAsync 方法可将向 SetHtmlContent 提供的 HTML 注入到匹配的 <address> 元素中。 在以下情况下进行注入:

  • 执行上下文的 TagName 属性值等于 address
  • 相应的 <address> 元素具有 printable 属性。

例如,在处理以下 <address> 元素时,if 语句的计算结果为 true:

<address printable>
    One Microsoft Way<br />
    Redmond, WA 98052-6399<br />
    <abbr title="Phone">P:</abbr>
    425.555.0100
</address>

其他资源