Componentes del asistente de etiquetas en ASP.NET Core

Por Scott Addie y Fiyaz Bin Hasan

El componente de un asistente de etiquetas es un asistente de etiquetas que permite modificar o agregar con condiciones elementos HTML a partir del código del lado servidor. Esta característica está disponible en ASP.NET Core 2.0 o versiones posteriores.

ASP.NET Core incluye dos componentes de asistente de etiquetas integrados: head y body. Se encuentran en el espacio de nombres Microsoft.AspNetCore.Mvc.Razor.TagHelpers y pueden usarse tanto en MVC como en Razor Pages. Los componentes de asistente de etiquetas no requieren el registro en la aplicación en _ViewImports.cshtml.

Vea o descargue el código de ejemplo (cómo descargarlo)

Casos de uso

Dos casos de uso comunes de componentes de asistente de etiquetas incluyen:

  1. Insertar <link> en <head>.
  2. Insertar <script> en <body>.

En las secciones siguientes se describen estos casos de uso.

Insertar en el elemento de encabezado HTML

Dentro del elemento <head> HTML, los archivos CSS suelen importarse con el elemento <link> HTML. El código siguiente inserta un elemento <link> en el elemento <head> con el componente de asistente de etiquetas 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;
        }
    }
}

En el código anterior:

  • AddressStyleTagHelperComponent implementa TagHelperComponent. La abstracción:
    • Permite la inicialización de la clase con TagHelperContext.
    • Permite usar componentes de asistente de etiquetas para agregar o modificar elementos HTML.
  • La propiedad Order define el orden en el que se representan los componentes. Order es necesario cuando hay varios usos de los componentes de asistente de etiquetas en una aplicación.
  • ProcessAsync compara el valor de propiedad TagName del contexto de ejecución con head. Si la comparación se evalúa como true, el contenido del campo _style se inserta en el elemento <head> HTML.

Insertar en el elemento del cuerpo HTML

El componente de asistente de etiquetas body puede insertar un elemento <script> en el elemento <body>. El código siguiente demuestra esta 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);
            }
        }
    }
}

Se usa un archivo HTML independiente para almacenar el elemento <script>. El archivo HTML hace que el código sea más limpio y más fácil de mantener. El código anterior lee el contenido de TagHelpers/Templates/AddressToolTipScript.html y lo anexa con la salida del asistente de etiquetas. El archivo AddressToolTipScript.html incluye el marcado siguiente:

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

El código anterior enlaza un widget de información sobre herramientas de arranque a cualquier elemento <address> que incluye un atributo printable. El efecto es visible cuando el puntero del mouse se sitúa sobre el elemento.

Registro de un componente

Se debe agregar un componente de asistente de etiquetas a la colección de componentes de asistente de etiquetas de la aplicación. Hay tres maneras de agregarlo a la colección:

Registro mediante el contenedor de servicios

Si la clase de componente de asistente de etiquetas no se administran con ITagHelperComponentManager, se debe registrar con el sistema de inserción de dependencias (DI). El código Startup.ConfigureServices siguiente registra las clases AddressStyleTagHelperComponent y AddressScriptTagHelperComponent con una duración transitoria:

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 mediante un archivo de Razor

Si el componente de asistente de etiquetas no está registrado con la inserción de dependencias, se puede registrar desde una página de Razor Pages o desde una vista de MVC. Esta técnica se usa para controlar el orden de ejecución del componente y el marcado insertado desde un archivo de Razor.

ITagHelperComponentManager se usa para agregar componentes de asistente de etiquetas o para quitarlos de la aplicación. El código siguiente demuestra esta técnica con 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));
}

En el código anterior:

  • La directiva @inject proporciona una instancia de ITagHelperComponentManager. La instancia está asignada a una variable denominada manager para el acceso descendente en el archivo de Razor.
  • Se agrega una instancia de AddressTagHelperComponent a la colección de componentes de asistente de etiquetas de la aplicación.

AddressTagHelperComponent se modifica para alojar un constructor que acepta los parámetros markup y order:

private readonly string _markup;

public override int Order { get; }

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

El parámetro markup proporcionado se utiliza en ProcessAsync como sigue:

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 con el modelo de página o controlador

Si el componente de asistente de etiquetas no está registrado con la inserción de dependencias, se puede registrar desde un modelo de página de Razor Pages o desde un controlador de MVC. Esta técnica es útil para separar la lógica de C# de archivos de Razor.

La inserción del constructor se utiliza para acceder a una instancia de ITagHelperComponentManager. El componente de asistente de etiquetas se agrega a la colección de componentes de asistente de etiquetas de la instancia. El modelo de página de Razor Pages siguiente muestra esta técnica con 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));
    }
}

En el código anterior:

  • La inserción del constructor se utiliza para acceder a una instancia de ITagHelperComponentManager.
  • Se agrega una instancia de AddressTagHelperComponent a la colección de componentes de asistente de etiquetas de la aplicación.

Creación de un componente

Para crear un componente de asistente de etiquetas personalizado:

El código siguiente crea un componente de asistente de etiquetas personalizado que tiene como destino el elemento <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)
        {
        }
    }
}

Use el componente de asistente de etiquetas address personalizado para insertar el marcado HTML como sigue:

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

El método ProcessAsync anterior inserta el elemento HTML proporcionado a SetHtmlContent en el elemento <address> coincidente. La inserción se produce cuando:

  • El valor de propiedad TagName del contexto de ejecución es igual a address.
  • El elemento <address> correspondiente tiene un atributo printable.

Por ejemplo, la instrucción if se evalúa como true al procesar el siguiente elemento <address>:

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

Recursos adicionales