Вспомогательные компоненты тегов в ASP.NET Core

Авторы: Скотт Адди (Scott Addie) и Хасан Фияз Бин (Hasan Fiyaz Bin)

Вспомогательный компонент тегов позволяет на основе условий изменять или добавлять элементы HTML из серверного кода. Эта функция доступна в ASP.NET Core 2.0 и более поздних версий.

ASP.NET Core включает в себя два встроенных вспомогательных компонента тегов: head и body. Они находятся в пространстве имен и могут использоваться как Microsoft.AspNetCore.Mvc.Razor.TagHelpers в MVC, так и Razor в страницах. Компоненты вспомогательного тега не требуют регистрации в приложении _ViewImports.cshtml.

Просмотреть или скачать образец кода (описание загрузки)

Случаи использования

Два распространенных варианта для использования вспомогательных компонентов тегов:

  1. Внедрение <link> в <head>.
  2. Внедрение <script> в <body>.

Эти варианты использования описаны в следующих разделах.

Внедрение в элемент HTML "head"

Внутри элемента HTML <head> файлы CSS обычно импортируются с помощью элемента HTML <link>. Следующий код позволяет вставить элемент <link> в элемент <head> с помощью вспомогательного компонента тегов 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);
            }
        }
    }
}

Для хранения элемента <script> используется отдельный HTML-файл. Этот 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>

Приведенный выше код привязывает мини-приложение подсказки начальной загрузки к любому элементу <address>, который содержит атрибут printable. Результат можно проследить при наведении указателя мыши на соответствующий элемент.

Регистрация компонента

Вспомогательные компоненты тегов необходимо добавить в коллекцию вспомогательных компонентов тегов приложения. Добавить его в коллекцию можно тремя способами:

Регистрация с помощью контейнера служб

Если ITagHelperComponentManager не управляет классом вспомогательного компонента тегов, класс нужно зарегистрировать в системе внедрения зависимостей (DI). Следующий код Startup.ConfigureServices позволяет зарегистрировать классы AddressStyleTagHelperComponent и AddressScriptTagHelperComponent с ограниченным временем существования:

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 странице страницы или в представлении 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 вносятся изменения для создания конструктора, который принимает параметры markup и order:

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}");
    }
}

Регистрация с помощью модели страницы или контроллера

Если компонент вспомогательной Razor функции тега не зарегистрирован в di, его можно зарегистрировать в модели страниц страницы или контроллере 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 добавляется в коллекцию вспомогательных компонентов тегов.

Создание компонента

Чтобы создать пользовательский вспомогательный компонент тегов, выполните следующие действия:

Следующий код позволяет создать пользовательский вспомогательный компонент тегов, который работает с элементом HTML <address>:

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.

Например, инструкция if возвращает значение TRUE при обработке следующего элемента <address>:

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

Дополнительные ресурсы