Taghilfsprogrammkomponenten in ASP.NET Core

Von Scott Addie und Fiyaz Bin Hasan

Eine Taghilfsprogrammkomponente ist ein Taghilfsprogramm, mit dem Sie HTML-Elemente aus serverseitigem Code bedingt ändern oder hinzufügen können. Dieses Feature ist in ASP.NET Core 2.0 oder höher verfügbar.

ASP.NET Core enthält zwei integrierte Taghilfsprogrammkomponenten: head und body. Sie befinden sich im Namespace Microsoft.AspNetCore.Mvc.Razor.TagHelpers und können in MVC und Razor Pages verwendet werden. Taghilfsprogrammkomponenten erfordern keine Registrierung bei der App in _ViewImports.cshtml.

Anzeigen oder Herunterladen von Beispielcode (Vorgehensweise zum Herunterladen)

Anwendungsfälle

Zwei gängige Anwendungsfälle für Taghilfsprogrammkomponenten sind:

  1. Einfügen eines <link> in <head>.
  2. Einfügen eines <script> in <body>.

In den folgenden Abschnitten werden diese Anwendungsfälle beschrieben.

Einfügen in HTML-head-Element

Im HTML-<head>-Element werden CSS-Dateien häufig mit dem HTML-<link>-Element importiert. Der folgende Code fügt ein <link>-Element in das <head>-Element mit der head-Taghilfsprogrammkomponente ein:

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

Für den Code oben gilt:

  • AddressStyleTagHelperComponent implementiert TagHelperComponent. Die Abstraktion:
    • Ermöglicht die Initialisierung der Klasse mit einem TagHelperContext.
    • Ermöglicht die Verwendung von Taghilfsprogrammkomponenten zum Hinzufügen oder Ändern von HTML-Elementen.
  • Die Order-Eigenschaft definiert die Reihenfolge, in der die Komponenten gerendert werden. Order ist erforderlich, wenn mehrere Verwendungsmöglichkeiten für Taghilfsprogrammkomponenten in einer App vorhanden sind.
  • ProcessAsync vergleicht den TagName-Eigenschaftswert des Ausführungskontexts mit head. Wenn der Vergleich zu TRUE ausgewertet wird, wird der Inhalt des _style-Felds in das HTML<head>-Element eingefügt.

Einfügen in HTML-body-Element

Die body-Taghilfsprogrammkomponente kann ein <script>-Element in das <body>-Element einfügen. Im folgenden Code wird diese Technik veranschaulicht:

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

Eine separate HTML-Datei wird zum Speichern des <script>-Elements verwendet. Durch die HTML-Datei wird der Code übersichtlicher und besser verwaltbar. Der vorangehende Code liest den Inhalt von TagHelpers/Templates/AddressToolTipScript.html und fügt diesen mit der Ausgabe des Taghilfsprogramms an. Diese Datei AddressToolTipScript.html enthält das folgende Markup:

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

Der vorangehende Code bindet ein Bootstrap-QuickInfo-Widget an ein beliebiges <address>-Element, das ein printable-Attribut enthält. Der Effekt wird sichtbar, wenn ein Mauszeiger über das Element bewegt wird.

Registrieren einer Komponente

Eine Taghilfsprogrammkomponente muss der Sammlung „Taghilfsprogrammkomponenten“ der App hinzugefügt werden. Es gibt drei Möglichkeiten, der Sammlung Taghilfsprogrammkomponenten hinzuzufügen:

Registrierung über Dienstcontainer

Wenn die Klasse der Taghilfsprogrammkomponente nicht mit ITagHelperComponentManager verwaltet wird, muss sie mit dem Abhängigkeitsinjektionssystem (Dependency Injection, DI) registriert werden. Der folgende Startup.ConfigureServices-Code registriert die AddressStyleTagHelperComponent- und AddressScriptTagHelperComponent- -Klassen mit einer vorübergehenden Lebensdauer:

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

Registrierung über Razor-Datei

Wenn die Taghilfsprogrammkomponente nicht mit Abhängigkeitsinjektion registriert wird, kann sie über eine Razor Pages-Seite oder eine MVC-Ansicht registriert werden. Diese Technik wird verwendet, um das injizierte Markup und die Reihenfolge der Komponentenausführung aus einer Razor-Datei zu steuern.

ITagHelperComponentManager dient zum Hinzufügen von Taghilfsprogrammkomponenten oder zum Entfernen aus der App. Im folgenden Code wird diese Technik mit AddressTagHelperComponent veranschaulicht:

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

Für den Code oben gilt:

  • Die @inject-Direktive stellt eine Instanz von ITagHelperComponentManager zur Verfügung. Die Instanz wird einer Variablen mit dem Namen manager für den Downstreamzugriff in der Razor-Datei zugewiesen.
  • Eine Instanz von AddressTagHelperComponent wird der Sammlung „Taghilfsprogrammkomponenten“ der App hinzugefügt.

AddressTagHelperComponent wird geändert, um einen Konstruktor zu berücksichtigen, der die markup- und order-Parameter annimmt:

private readonly string _markup;

public override int Order { get; }

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

Der bereitgestellte markup-Parameter wird in ProcessAsync wie folgt verwendet:

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

Registrierung über Seitenmodell oder Controller

Wenn die Taghilfsprogrammkomponente nicht mit Abhängigkeitsinjektion registriert wird, kann sie über ein Razor Pages-Seitenmodell oder einen MVC-Controller registriert werden. Diese Methode eignet sich zum Trennen von C#-Logik von Razor-Dateien.

Die Konstruktorinjektion wird verwendet, um auf eine Instanz von ITagHelperComponentManager zuzugreifen. Die Taghilfsprogrammkomponente wird der Sammlung „Taghilfsprogrammkomponenten“ der Instanz hinzugefügt. Das folgende Razor Pages-Seitenmodell veranschaulicht diese Technik mit 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));
    }
}

Für den Code oben gilt:

  • Die Konstruktorinjektion wird verwendet, um auf eine Instanz von ITagHelperComponentManager zuzugreifen.
  • Eine Instanz von AddressTagHelperComponent wird der Sammlung „Taghilfsprogrammkomponenten“ der App hinzugefügt.

Erstellen einer Komponente

So erstellen Sie eine benutzerdefinierte Taghilfsprogrammkomponente:

Der folgende Code erstellt eine benutzerdefinierte Taghilfsprogrammkomponente, das als Ziel das <address>-HTML-Element aufweist:

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

Verwenden Sie die benutzerdefinierte address-Taghilfsprogrammkomponente zum Einfügen von HTML-Markup wie hier gezeigt:

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

Die obige ProcessAsync-Methode injiziert den für SetHtmlContent bereitgestellten HTML-Code in das entsprechende <address>-Element. Die Einfügung tritt auf, wenn:

  • Der TagName-Eigenschaftswert des Ausführungskontexts address entspricht.
  • Das entsprechende <address>-Element über ein printable-Attribut verfügt.

Beispielsweise wird die if-Anweisung bei der Verarbeitung des folgenden <address>-Elements zu TRUE ausgewertet:

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

Zusätzliche Ressourcen