Partager via


Tag Helper Components dans ASP.NET Core

Par Scott Addie et Fiyaz Bin Hasan

Un Tag Helper Component est un Tag Helper qui vous permet de modifier ou d’ajouter des éléments HTML de manière conditionnelle à partir du code côté serveur. Cette fonctionnalité est disponible dans ASP.NET Core 2.0 ou version ultérieure.

ASP.NET Core inclut deux Tag Helper Components intégrés : head et body. Ils se trouvent dans l’espace de noms Microsoft.AspNetCore.Mvc.Razor.TagHelpers et peuvent être utilisés dans des pages MVC et Razor. Les composants de Tag Helper ne nécessitent pas d’inscription avec l’application dans _ViewImports.cshtml.

Affichez ou téléchargez l’exemple de code (procédure de téléchargement)

Cas d'utilisation

Voici deux cas d’usage courants des Tag Helper Components :

  1. Injecter un <link> dans l’élément <head>.
  2. Injecter un <script> dans l’élément <body>.

Les sections suivantes décrivent ces cas d’usage.

Injection dans l’élément head HTML

Dans l’élément <head> HTML, les fichiers CSS sont généralement importés avec l’élément <link> HTML. Le code suivant injecte un élément <link> dans l’élément <head> à l’aide du Tag Helper Component 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;
        }
    }
}

Dans le code précédent :

  • L'objet AddressStyleTagHelperComponent implémente l'objet TagHelperComponent. L’abstraction :
    • Permet l’initialisation de la classe avec un TagHelperContext.
    • Permet l’utilisation des Tag Helper Components pour ajouter ou modifier des éléments HTML.
  • La propriété Order définit l’ordre dans lequel les Components sont rendus. Order est requis quand il y a plusieurs utilisations des Tag Helper Components dans une application.
  • ProcessAsync compare la valeur de la propriété TagName du contexte d’exécution à head. Si le résultat de la comparaison est true, le contenu du champ _style est injecté dans l’élément <head> HTML.

Injection dans l’élément body HTML

Le Tag Helper Component body peut injecter un élément <script> dans l’élément <body>. Le code suivant illustre cette technique :

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

Un fichier HTML distinct est utilisé pour stocker l’élément <script>. Le fichier HTML rend le code plus lisible et plus facile à tenir à jour. Le code précédent lit le contenu de TagHelpers/Templates/AddressToolTipScript.html et l’ajoute à la sortie du Tag Helper. Le fichier AddressToolTipScript.html contient le balisage suivant :

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

Le code précédent lie un widget tooltip amorçable à tout élément <address> contenant un attribut printable. L’effet est visible quand l’utilisateur pointe sur l’élément avec la souris.

Inscrire un Component

Un Tag Helper Component doit être ajouté à la collection des Tag Helper Components de l’application. Cet ajout à la collection peut s’effectuer de trois façons :

Inscription par le biais d’un conteneur de services

Si la classe Tag Helper Component n’est pas managée avec ITagHelperComponentManager, elle doit être inscrite avec le système d’injection de dépendances. Le code Startup.ConfigureServices ci-dessous inscrit les classes AddressStyleTagHelperComponent et AddressScriptTagHelperComponent avec une durée de vie transitoire (transient) :

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

Inscription au moyen d’un fichier Razor

Si le composant de Tag Helper n’est pas inscrit avec le système d’injection de dépendances, il peut être inscrit depuis une page Razor ou une vue MVC. Cette technique permet de contrôler le balisage injecté et l’ordre d’exécution des composants à partir d’un fichier Razor.

ITagHelperComponentManager est utilisé pour ajouter ou supprimer des Tag Helper Components dans l’application. Le code suivant illustre cette technique avec 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));
}

Dans le code précédent :

  • La directive @inject fournit une instance de ITagHelperComponentManager. L’instance est assignée à une variable nommée manager pour l’accès en aval dans le fichier Razor.
  • Une instance de AddressTagHelperComponent est ajoutée à la collection des Tag Helper Components de l’application.

AddressTagHelperComponent est modifié pour contenir un constructeur qui accepte les paramètres markup et order :

private readonly string _markup;

public override int Order { get; }

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

Voici comment le paramètre markup s’utilise dans 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}");
    }
}

Inscription à l’aide d’un modèle de page ou d’un contrôleur

Si le composant de Tag Helper n’est pas inscrit avec le système d’injection de dépendances, il peut être inscrit à partir d’un modèle de page Razor ou d’un contrôleur MVC. Cette technique est utile pour séparer la logique C# des fichiers Razor.

L’injection de constructeur est utilisée pour accéder à une instance de ITagHelperComponentManager. Le Tag Helper Component est ajouté à la collection des Tag Helper Components de l’instance. Le modèle de page Razor ci-dessous illustre cette technique avec 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));
    }
}

Dans le code précédent :

  • L’injection de constructeur est utilisée pour accéder à une instance de ITagHelperComponentManager.
  • Une instance de AddressTagHelperComponent est ajoutée à la collection des Tag Helper Components de l’application.

Créer un Component

Pour créer un Tag Helper Component personnalisé :

Le code suivant crée un Tag Helper Component personnalisé qui cible l’élément 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)
        {
        }
    }
}

Utilisez le Tag Helper Component address personnalisé pour injecter le balisage HTML comme ceci :

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

La méthode ProcessAsync précédente injecte le code HTML fourni à SetHtmlContent dans l’élément <address> correspondant. L’injection se produit quand :

  • La propriété TagName du contexte d’exécution a la valeur address.
  • L’élément <address> correspondant a un attribut printable.

Par exemple, l’instruction if a la valeur true quand l’élément <address> suivant est exécuté :

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

Ressources supplémentaires