- PVSM.RU - https://www.pvsm.ru -

Готовим ASP.NET Core: создаем собственный Tag Helper

Мы продолжаем нашу колонку по теме ASP.NET Core публикацией от Станислава Ушакова ( JustStas [1]) — team lead из компании DataArt. В статье Стас рассказывает о способах создания своих собственных тег-хелперов в ASP.NET Core. Предыдущие статьи из колонки всегда можно прочитать по ссылке #aspnetcolumn [2] — Владимир Юнев

В прошлый раз [3] мы рассмотрели уже существующие в ASP.NET Core 1.0 тег-хэлперы. В этот раз мы рассмотрим создание собственных тег-хэлперов, что может упростить генерацию небольших переиспользуемых участков HTML-разметки.

Готовим ASP.NET Core: создаем собственный Tag Helper - 1

Введение

Формально тег-хэлпером является любой класс, наследующий от интерфейса ITagHelper.На практике напрямую от ITagHelper наследовать не надо, удобнее наследовать от класса TagHelper, переопределяя метод Process / ProcessAsync, которые рассмотрим по ходу статьи.

aspnetcolumngithub [4]Совет! Вы можете попробовать все самостоятельно или загрузив исходный код из GitHub https://github.com/StanislavUshakov/CustomTagHelpers [4].

Инфраструктура

Мы будем использовать Visual Studio 2015 Community Edition. Создадим новый ASP.NET 5 сайт (рисунок 1).

Готовим ASP.NET Core: создаем собственный Tag Helper - 3

Чтобы не начинать с полностью пустого проекта, выберем шаблон“WebApplication” (рисунок 2), но удалим аутентификацию: “Change Authentication”, далее выбрать “No authentication”.

Готовим ASP.NET Core: создаем собственный Tag Helper - 4

В результате получим проект с уже подключенным MVC, одним контроллером и тремя действиями: Index, About, Contact. Добавим новую папку в проект, назовем ее TagHelpers: необязательно называть ее именно так, но можно сказать, что это договоренность, и лучше ее соблюдать – все собственноручно написанные тег-хэлперы класть в эту папку. Получим следующую структуру проекта (рисунок 3).

Готовим ASP.NET Core: создаем собственный Tag Helper - 5

EmailTagHelper

В коде представления Contact.cshtml можно увидеть следующую разметку, содержащую адреса электронной почты.

<address>
<strong>Support:</strong><a href="mailto:Support@example.com">Support@example.com</a><br />
<strong>Marketing:</strong><a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>

Создадим тег-хэлпер email, который будем использовать следующим образом:

<email>Support</email>

Генерируя следующую HTML разметку:

<a href="mailto:Support@example.com ">Support@example.com</a>

Такой тег-хэлпер пригодится, если надо генерировать много ссылок на адреса электронной почты одного домена. Добавим новый файл в папку TagHelpers, назовем его EmailTagHelper.cs.

usingMicrosoft.AspNet.Razor.TagHelpers;

namespaceCustomTagHelpersAndViewComponents.TagHelpers
{
    public class EmailTagHelper : TagHelper
    {
        public string MailTo { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "a";    // заменим тег <email> на тег <a>
        }
    }
}

Уже на примере этого самого простого тег-хэлпера можно увидеть следующее:

  • Тег-хэлперы придерживаются следующего правила именования по умолчанию: тег, используемый в cshtml представлениях, равен имени класса тег-хэлпера минус TagHelper. В нашем случае используем тег email. Дальше будет показано, как можно переопределить это поведение по умолчанию. Можно было бы назвать наш тег-хэлпер и просто Email, механизм работы остался бы тот же, но по правилу именования постфикс TagHelper требуется добавлять.
  • Переопределяя метод Process, мы указываем какой код будет выполняться нашим тег-хэлпером. Также класс TagHelper определяет метод ProcessAsync с такими же параметрами.
  • Параметр context содержит информацию о контексте выполнения текущего HTML тега.
  • Парметр output хранит данные об оригинальном HTML элементе, который был использован в представлении, и его содержимом.

Для того, чтобы использовать созданный нами тег-хэлпер, добавим в файл Views/_ViewImports.cshtml директиву для его подключения.

@using CustomTagHelpersAndViewComponents
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
@addTagHelper "*, CustomTagHelpersAndViewComponents"

Мы используем подстановочный символ, чтобы добавить в область видимости сразу все тег-хэлперы из сборки. Первый аргумент директивы @addTagHelper — это имя тег-хэлпера (или звездочка), второй – сборка, в которой они ищутся. Если мы хотим добавить один тег-хэлпер, а не все сразу, то надо указать его полное имя (FQN – fully qualified name):

@using CustomTagHelpersAndViewComponents
@addTagHelper "*, Microsoft.AspNet.Mvc.TagHelpers"
@addTagHelper "CustomTagHelpersAndViewComponents.TagHelpers.EmailTagHelper, CustomTagHelpersAndViewComponents"

Теперь обновим код представления Views/Home/Contact.cshtml, переписав ссылки на электронную почту:

<address>
    <strong>Support:</strong><email>Support</email><br />
    <strong>Marketing:</strong><email>Marketing</email>
</address>

Visual Studio 2015 замечательно подсвечивает тег-хэлперы, наш вновь созданный – не исключение:

Готовим ASP.NET Core: создаем собственный Tag Helper - 6

Откроем сайт в браузере и увидим, что тег email заменился на тег a, самое время добавить необходимую логику для генерации правильной ссылки на электронную почту. Необходимую информацию будем передавать с помощью атрибута email-to, домен электронной почты будет задан константой в тег-хэлпере.

using Microsoft.AspNet.Razor.TagHelpers;

namespace CustomTagHelpersAndViewComponents.TagHelpers
{
    public class EmailTagHelper : TagHelper
    {
        private const string EmailDomain = "example.com";

        public string MailTo { get; set; }

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "a";
            string address = MailTo + "@" + EmailDomain;
            output.Attributes["href"] = "mailto:" + address;
            output.Content.SetContent(address);
        }
    }
}

Отметим интересные моменты этого кода. Первый момент: свойство MailTo из PascalCase в C# коде станет low-kebab-case [5] в коде представления: email-to, как это и принято сейчас во фронт-енде. Последняя строка метода устанавливает содержимое созданного тега. Наряду с таким синтаксисом добавления атрибутов output.Attributes[«href»] = «mailto:» + address; можно использовать метод output.Attributes.Add.
Теперь обновим представление Views/Home/Contact.cshtml:

<address>
    <strong>Support:</strong> <email mail-to="Support"></email><br />
    <strong>Marketing:</strong> <email mail-to="Marketing"></email>
</address>

В результате будет сгенерирована следующая HTML разметка:

<address>
    <strong>Support:</strong> <a href="mailto:Support@example.com">Support@example.com</a><br>
    <strong>Marketing:</strong> <a href="mailto:Marketing@example.com">Marketing@example.com</a>
</address>

Заметим, что сейчас мы не можем написать <email mail-to=«Support» />, для этого надо добавить специальный атрибут, подключив сборку Microsoft.AspNet.Razor.TagHelpers:

[HtmlTargetElement("email", TagStructure = TagStructure.WithoutEndTag)]

Также вместо TagStructure.WithoutEndTag можно использовать значение TagStructure.NormalOrSelfClosing, в этом случае можно будет писать как <email></email>, так и <email />.

Тег-хэлпер bold

Напишем простой тег-хэлпер, который можно будет применять к различным HTML тегам. Пусть он превращает текст в полужирный для всех HTML тегов, к которым применяется. Нам опять потребуется атрибут HtmlTargetElement. Добавим в папку TagHelpers новый файл BoldTagHelper.cs со следующим содержимым:

using Microsoft.AspNet.Razor.TagHelpers;

namespace CustomTagHelpersAndViewComponents.TagHelpers
{
    [HtmlTargetElement(Attributes = "bold")]
    public class BoldTagHelper : TagHelper
    {
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.Attributes.RemoveAll("bold");
            output.PreContent.SetHtmlContent("<strong>");
            output.PostContent.SetHtmlContent("</strong>");
        }
    }
}

Теперь в атрибут HtmlTargetElement мы не передаем имя тега, к которому тег-хэлпер применяется, а имя HTML атрибута, который можно добавлять к разным HTML тегам. Поменяем разметку в представлении About.cshtml:

<p bold>Use this area to provide additional information.</p>

В результате сгенерируется следующая HTML разметка:

<p><strong>Use this area to provide additional information.</strong></p>

Однако, если мы попробуем использовать bold не как атрибут, а как имя HTML тега, код тег-хэлпера вызван не будет. Это можно исправить, добавив к классу тег-хэлпера атрибут HtmlTargetElement еще раз, только передав ему имя тега bold:

[HtmlTargetElement("bold")]

Теперь в представлении мы можем написать и так:

<bold>Use this area to provide additional information.</bold >

Если мы добавим сразу оба атрибута HtmlTargetElement к нашему классу тег-хэлпера:

[HtmlTargetElement("bold")]
[HtmlTargetElement(Attributes = "bold")]

То они будут между собой связаны логическим ИЛИ: или тег bold, или атрибут bold. Можно объединять больше двух атрибутов HtmlTargetElement, все они будут связаны через ИЛИ. Если мы хотим связать их через И: применять к тегу bold с атрибутом bold, нужно объединить эти условия в одном атрибуте HtmlTargetElement:

[HtmlTargetElement("bold", Attributes = "bold")]

Теперь рассмотрим код внутри метода Process. Метод output.Attributes.RemoveAll(«bold»); удаляет атрибут bold у HTML тега. Затем мы должны все содержимое тега обрамить тегом strong. Для этого используем свойства PreContent и PostContent объекта output. Они позволяют добавить новое содержимое внутри тега, но до основного содержимого и сразу после. Схематично это выглядит вот так:

<p bold>PRECONTENT Use this area to provide additional information. POST_CONTENT</p>

Если же мы хотим добавить новое содержимое вне тега, надо использовать свойства PreElement и PostElement, тогда получим следующее:

PRE_ELEMENT<p bold>Use this area to provide additional information.</p>POST_ELEMENT

И не забываем использовать метод SetHtmlContent, а не SetContent, если мы хотим добавить HTML теги, иначе метод SetContent перекодирует переданную строку и тег strong не применится.

Асинхронный тег-хэлпер copyright

До этого мы перегружали метод Process, сейчас перегрузим его асинхронную версию: метод ProcessAsync. Напишем простой тег-хэлпер, который будет выводить такую разметку:

<p>© 2016 MyCompany</p>

Добавим в папку TagHelpers новый файл CopyrightTagHelper.cs:

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

namespace CustomTagHelpersAndViewComponents.TagHelpers
{
    /// <summary>
    /// Custom tag helper for generating copyright information. Content inside will be added after: © Year
    /// </summary>
    [HtmlTargetElement("copyright")]
    public class CopyrightTagHelper : TagHelper
    {
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var content = await output.GetChildContentAsync();
            string copyright = $"<p>© {DateTime.Now.Year} {content.GetContent()}</p>";
            output.Content.SetHtmlContent(copyright);
        }
    }
}

В отличие от синхронного метода Process, метод ProcessAsync возвращает объект типа Task. Также к методу мы добавили модификатор async, потому что внутри метода содержимое тега получаем с помощью конструкции await output.GetChildContentAsync();. Затем с помощью нового механизма строк форматирования в C# 6 формируем содержимое тега и добавляем его с помощью метода output.Content.SetHtmlContent(copyright);. Во всех остальных аспектах, этот простой асинхронный тег-хэлпер не отличается от синхронного.

Best practices и выводы

Чтобы написать хороший тег-хэлпер, надо знать, как написаны отличные. Для этого посмотрим на стандартные тег-хэлперы, доступные в MVC: Microsoft.AspNetCore.Mvc.TagHelpers [6]. И добавим в свои созданные тег-хэлперы достаточное количество комментариев и проверок.

Весь исходный код проекта досутпен в ГитХабе по адресу: Custom Tag Helpers [4].

Собственные тег-хэлперы позволяют оптимизировать часто встречающие задачи по генерации HTML разметки небольшого размера. Если же вы хотите инкапсулировать более сложную логику представления, лучше использовать View Components [7].

Пробуйте ASP.NET Core 1.0, сообщайте о багах, добавляйте фичи!

Авторам

Друзья, если вам интересно поддержать колонку своим собственным материалом, то прошу написать мне на vyunev@microsoft.com [8] для того чтобы обсудить все детали. Мы разыскиваем авторов, которые могут интересно рассказать про ASP.NET и другие темы.

Готовим ASP.NET Core: создаем собственный Tag Helper - 7

Об авторе

Станислав Ушаков
Senior .Net Developer / Team lead в DataArt

Родился, учился в Воронеже. .Net'ом профессионально занимаюсь уже больше 6 лет, до этого даже MFC видел. Люблю программировать (недавно дошли руки до Ардуино, играюсь), читать книги (особенно бумажные), играть в ЧтоГдеКогда (спортивное), учить и разбираться во всем новом. Хочу наконец защитить написанную кандидатскую.

JustStas [1]

Анонс! Глубокий интенсив на конференции DevCon 2016

image

Мы рады объявить о проведении глубокого интенсив-курса по ASP.NET Core в рамках [конференции DevCon 2016](https://events.techdays.ru/DevCon/2016/registration). Этот курс будет проходить во второй день конференции и займет полный день конференции в течение 6 часов.

Разработка современных веб-приложений на открытой платформе ASP.NET 5
В рамках этого интенсива участники примут участие в увлекательном и полном приключений путешествии, посвященном разработке веб-приложений на новейшей платформе ASP.NET 5. Мы пройдем весь путь от нуля до полноценного приложения, развернутого в облаке. И по дороге участники смогут остановиться для изучения внутреннего устройства ASP.NET, работы с реляционными и NoSQL базами данных с помощью Entity Framework 7, разработки приложений на фреймворке MVC, построении моделей, представлений и бизнес-логики, создании REST API, организации процессов непрерывной разработки и тестирования с помощью Git и Visual Studio Online, а также развертывания с помощью Azure и контейнеров Docker. В конце путешествия все участники пройдут посвящение и станут заслуженными рыцарями ASP.NET.

Регистрация на конференцию DevCon 2016 уже открыта! Регистрируйтесь здесь [9].

Автор: Microsoft

Источник [10]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/net/113855

Ссылки в тексте:

[1] JustStas: https://habrahabr.ru/users/juststas/

[2] #aspnetcolumn: http://habrahabr.ru/search/?q=%5B%23aspnetcolumn%5D&target_type=posts

[3] прошлый раз: https://habrahabr.ru/post/276277/

[4] Image: https://github.com/StanislavUshakov/CustomTagHelpers

[5] low-kebab-case: http://stackoverflow.com/questions/11273282/whats-the-name-for-hyphen-separated-case/12273101#12273101

[6] Microsoft.AspNetCore.Mvc.TagHelpers: https://github.com/aspnet/Mvc/tree/dev/src/Microsoft.AspNetCore.Mvc.TagHelpers

[7] View Components: http://docs.asp.net/projects/mvc/en/latest/views/view-components.html

[8] vyunev@microsoft.com: mailto:vyunev@microsoft.com

[9] Регистрируйтесь здесь: https://events.techdays.ru/DevCon/2016/registration

[10] Источник: https://habrahabr.ru/post/278251/