Componentes do Auxiliar de Marca no ASP.NET Core

Por Scott Addie e Fiyaz Bin Hasan

Um Componente do Auxiliar de Marca é um Auxiliar de Marca que permite que você modifique ou adicione condicionalmente elementos HTML de código do lado do servidor. Esse recurso está disponível no ASP.NET Core 2.0 ou posterior.

O ASP.NET Core inclui dois Componentes de Auxiliar de Marca internos: head e body. Eles estão localizados no namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers e pode ser usados no MVC e no Razor Pages. Componentes do Auxiliar de Marca não requerem registro com o aplicativo em _ViewImports.cshtml.

Exibir ou baixar código de exemplo (como baixar)

Casos de uso

Dois casos de uso comum dos Componentes do Auxiliar de Marca incluem:

  1. Injetar um <link> no <head>.
  2. Injetar um <script> no <body>.

As seções a seguir descrevem esses casos de uso.

Injetar no elemento HTML head

Dentro do elemento HTML <head>, os arquivos CSS são geralmente importados com o elemento HTML <link>. O código a seguir injeta um elemento <link> no elemento <head> usando o Componente do Auxiliar de Marca 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;
        }
    }
}

No código anterior:

  • AddressStyleTagHelperComponent implementa TagHelperComponent. A abstração:
    • Permite a inicialização da classe com um TagHelperContext.
    • Habilita o uso de Componentes do Auxiliar de Marca para adicionar ou modificar elementos HTML.
  • A propriedade Order define a ordem na qual os Componentes são renderizados. Order é necessário quando há vários usos de Componentes do Auxiliar de Marca em um aplicativo.
  • ProcessAsync compara o valor da propriedade TagName do contexto de execução com head. Se a comparação for avaliada como verdadeira, o conteúdo do campo _style será injetado no elemento HTML <head>.

Injetar no elemento HTML body

O Componente do Auxiliar de Marca body pode injetar um elemento <script> no elemento <body>. O código a seguir demonstra essa técnica:

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

Um arquivo HTML separado é usado para armazenar o elemento <script>. O arquivo HTML torna o código mais limpo e mais sustentável. O código anterior lê o conteúdo de TagHelpers/Templates/AddressToolTipScript.html e acrescenta-o com a saída do Auxiliar de Marca. O arquivo AddressToolTipScript.html inclui a seguinte marcação:

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

O código anterior associa um widget de Dica de ferramenta de inicialização a qualquer elemento <address> que inclua um atributo printable. O efeito é visível quando um ponteiro do mouse focaliza o elemento.

Registrar um componente

Um Componente do Auxiliar de Marca precisa ser adicionado à coleção de Componentes do Auxiliar de Marca do aplicativo. Há três maneiras de adicioná-lo à coleção:

Registro por contêiner de serviços

Se a classe do Componente do Auxiliar de Marca não for gerenciada com ITagHelperComponentManager, ela precisará ser registrada com o sistema de DI (injeção de dependência). O código Startup.ConfigureServices a seguir registra as classes AddressStyleTagHelperComponent e AddressScriptTagHelperComponent com um tempo de vida transitório:

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

Registro por arquivo Razor

Se o Componente do Auxiliar de Marca não estiver registrado com DI, ele poderá ser registrado por uma página do Razor Pages ou uma exibição do MVC. Essa técnica é usada para controlar a marcação injetada e o pedido de execução do componente de um arquivo Razor.

ITagHelperComponentManager é usado para adicionar Componentes do Auxiliar de Marca ou removê-los do aplicativo. O código a seguir demonstra essa técnica com 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));
}

No código anterior:

  • A diretiva @inject fornece uma instância de ITagHelperComponentManager. A instância é atribuída a uma variável chamada manager para acesso downstream no arquivo Razor.
  • Uma instância do AddressTagHelperComponent é adicionada à coleção de Componentes do Auxiliar de Marca do aplicativo.

AddressTagHelperComponent é modificado para acomodar um construtor que aceita os parâmetros markup e order:

private readonly string _markup;

public override int Order { get; }

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

O parâmetro markup fornecido é usado em ProcessAsync da seguinte maneira:

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

Registro por Modelo de página ou controlador

Se o Componente do Auxiliar de Marca não estiver registrado com DI, ele poderá ser registrado por um modelo de página do Razor Pages ou um controlador MVC. Essa técnica é útil para separar a lógica C# de arquivos Razor.

A injeção do construtor é usada para acessar uma instância de ITagHelperComponentManager. Um Componente do Auxiliar de Marca é adicionado à coleção de Componentes do Auxiliar de Marca da instância. O modelo de página do Razor Pages a seguir demonstra essa técnica com 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));
    }
}

No código anterior:

  • A injeção do construtor é usada para acessar uma instância de ITagHelperComponentManager.
  • Uma instância do AddressTagHelperComponent é adicionada à coleção de Componentes do Auxiliar de Marca do aplicativo.

Criar um componente

Para criar um Componente do Auxiliar de Marca personalizado:

O código a seguir cria um Componente do Auxiliar de Marca personalizado que tem como destino o elemento 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)
        {
        }
    }
}

Usar o Componente do Auxiliar de Marca address personalizado para inserir uma marcação HTML da seguinte maneira:

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

O método ProcessAsync anterior injeta o HTML fornecido para SetHtmlContent no elemento <address> correspondente. A injeção ocorre quando:

  • O valor da propriedade TagName do contexto de execução é igual a address.
  • O elemento <address> correspondente tem um atributo printable.

Por exemplo, a instrução if é avaliada como verdadeira ao processar o seguinte elemento <address>:

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

Recursos adicionais