Créer des Tag Helpers dans ASP.NET Core

Par Rick Anderson

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

Bien démarrer avec les Tag Helpers

Ce didacticiel fournit une introduction à la programmation des Tag Helpers. Introduction aux Tag Helpers décrit les avantages offerts par les Tag Helpers.

Un Tag Helper est toute classe qui implémente l’interface ITagHelper. Toutefois, quand vous créez un Tag Helper, vous dérivez généralement de TagHelper, ce qui vous permet d’accéder à la méthode Process.

  1. Créez un projet ASP.NET Core nommé AuthoringTagHelpers. Vous n’aurez pas besoin d’authentification pour ce projet.

  2. Créez un dossier pour stocker les Tag Helpers appelé TagHelpers. Le dossier TagHelpers n’est pas obligatoire, mais il est judicieux de le créer. Commençons à présent à écrire quelques Tag Helpers simples.

Tag Helper minimal

Dans cette section, vous écrivez un Tag Helper qui met à jour une balise e-mail. Par exemple :

<email>Support</email>

Le serveur utilise notre Tag Helper e-mail pour convertir ce balisage en code suivant :

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

Autrement dit, une balise d’ancrage qui en fait un lien e-mail. Vous pouvez effectuer cette opération si vous écrivez un moteur de blog qui doit envoyer des e-mails pour le marketing, le support et d’autres contacts, tous dans le même domaine.

  1. Ajoutez la classe EmailTagHelper suivante au dossier TagHelpers.

    
    using Microsoft.AspNetCore.Razor.TagHelpers;
    using System.Threading.Tasks;
    
    namespace AuthoringTagHelpers.TagHelpers
    {
        public class EmailTagHelper : TagHelper
        {
            public override void Process(TagHelperContext context, TagHelperOutput output)
            {
                output.TagName = "a";    // Replaces <email> with <a> tag
            }
        }
    }
    
    • Les Tag Helpers utilisent une convention de nommage qui cible des éléments du nom de classe racine (moins la partie TagHelper du nom de classe). Dans cet exemple, comme le nom racine de EmailTagHelper est email, la balise <email> est ciblée. Cette convention de nommage doit fonctionner pour la plupart des Tag Helpers et je vous montrerai plus tard comment la remplacer.

    • La classe EmailTagHelper est dérivée de TagHelper. La classe TagHelper fournit des méthodes et propriétés pour l’écriture des Tag Helpers.

    • La méthode Process substituée contrôle ce que fait le Tag Helper lors de son exécution. La classe TagHelper fournit également une version asynchrone (ProcessAsync) avec les mêmes paramètres.

    • Le paramètre de contexte pour Process (et ProcessAsync) contient des informations associées à l’exécution de la balise HTML actuelle.

    • Le paramètre de sortie pour Process (et ProcessAsync) contient un élément HTML avec état représentatif de la source d’origine utilisée pour générer une balise HTML et le contenu.

    • Le nom de notre classe comporte un suffixe TagHelper, ce qui n’est pas obligatoire, mais est considéré comme une bonne pratique. Vous pouvez déclarer la classe comme suit :

    public class Email : TagHelper
    
  2. Afin de rendre la classe EmailTagHelper disponible pour toutes les vues Razor, ajoutez la directive addTagHelper au fichier Views/_ViewImports.cshtml :

    @using AuthoringTagHelpers
    @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    @addTagHelper *, AuthoringTagHelpers
    

    Le code ci-dessus utilise la syntaxe d’expressions génériques pour spécifier que tous les Tag Helpers dans notre assembly seront disponibles. La première chaîne après @addTagHelper désigne le Tag Helper à charger (utilisez « * » pour tous les Tag Helpers), et la deuxième chaîne « AuthoringTagHelpers » indique l’assembly dans lequel se trouve le Tag Helper. En outre, notez que la deuxième ligne introduit les Tag Helpers d’ASP.NET Core MVC à l’aide de la syntaxe de caractères génériques (ceux-ci sont décrits dans Introduction aux Tag Helpers.) C’est la directive @addTagHelper qui rend le Tag Helper disponible pour la vue Razor. Sinon, vous pouvez fournir le nom qualifié complet d’un Tag Helper comme indiqué ci-dessous :

@using AuthoringTagHelpers
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper AuthoringTagHelpers.TagHelpers.EmailTagHelper, AuthoringTagHelpers

Pour ajouter un Tag Helper à une vue à l’aide d’un nom qualifié complet, ajoutez d’abord ce nom (AuthoringTagHelpers.TagHelpers.EmailTagHelper), puis le nom d’assembly (AuthoringTagHelpers, pas nécessairement namespace). La plupart des développeurs préfèrent utiliser la syntaxe d’expressions génériques. Introduction aux Tag Helpers décrit en détail l’ajout et la suppression de Tag Helpers, la hiérarchie et la syntaxe d’expressions génériques.

  1. Mettre à jour le balisage dans le fichier Views/Home/Contact.cshtml avec ces modifications :

    @{
        ViewData["Title"] = "Contact";
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <address>
        One Microsoft Way<br />
        Redmond, WA 98052<br />
        <abbr title="Phone">P:</abbr>
        425.555.0100
    </address>
    
    <address>
        <strong>Support:</strong><email>Support</email><br />
        <strong>Marketing:</strong><email>Marketing</email>
    </address>
    
  2. Exécutez l’application et utilisez votre navigateur favori pour afficher la source HTML afin de vérifier que les balises e-mail sont remplacées par un balisage d’ancrage (par exemple, <a>Support</a>). Support et Marketing s’affichent sous forme de liens, mais sans l’attribut href pour les rendre fonctionnels. Nous le corrigerons dans la section suivante.

SetAttribute et SetContent

Dans cette section, nous allons mettre à jour la classe EmailTagHelper afin qu’elle crée une balise d’ancrage valide pour les e-mails. Nous allons la mettre à jour pour extraire des informations d’une vue Razor (sous la forme d’un attribut mail-to) et les utiliser lors de la génération de l’ancre.

Mettez à jour la classe EmailTagHelper avec le code suivant :

public class EmailTagHelper : TagHelper
{
    private const string EmailDomain = "contoso.com";

    // Can be passed via <email mail-to="..." />. 
    // PascalCase gets translated into kebab-case.
    public string MailTo { get; set; }

    public override void Process(TagHelperContext context, TagHelperOutput output)
    {
        output.TagName = "a";    // Replaces <email> with <a> tag

        var address = MailTo + "@" + EmailDomain;
        output.Attributes.SetAttribute("href", "mailto:" + address);
        output.Content.SetContent(address);
    }
}
  • Les noms de propriété et de classe de casse Pascal pour les Tag Helpers sont convertis en casse kebab (mots séparés par des tirets). Par conséquent, pour utiliser l’attribut MailTo, vous employez l’équivalent <email mail-to="value"/>.

  • La dernière ligne définit le contenu terminé pour notre Tag Helper fonctionnel au minimum.

  • La ligne en surbrillance montre la syntaxe pour l’ajout d’attributs :

public override void Process(TagHelperContext context, TagHelperOutput output)
{
    output.TagName = "a";    // Replaces <email> with <a> tag

    var address = MailTo + "@" + EmailDomain;
    output.Attributes.SetAttribute("href", "mailto:" + address);
    output.Content.SetContent(address);
}

Cette approche fonctionne pour l’attribut « href » tant qu’il n’existe pas dans la collection d’attributs. Vous pouvez également utiliser la méthode output.Attributes.Add pour ajouter un attribut de Tag Helper à la fin de la collection des attributs de balise.

  1. Mettre à jour le balisage dans le fichier Views/Home/Contact.cshtml avec ces modifications :

    @{
        ViewData["Title"] = "Contact Copy";
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <address>
        One Microsoft Way Copy Version <br />
        Redmond, WA 98052-6399<br />
        <abbr title="Phone">P:</abbr>
        425.555.0100
    </address>
    
    <address>
        <strong>Support:</strong><email mail-to="Support"></email><br />
        <strong>Marketing:</strong><email mail-to="Marketing"></email>
    </address>
    
  2. Exécutez l’application et vérifiez qu’elle génère les liens corrects.

Notes

Si vous deviez écrire la fermeture automatique de la balise e-mail (<email mail-to="Rick" />), la sortie finale se fermerait aussi automatiquement. Pour activer la capacité d’écrire la balise avec uniquement une balise de début (<email mail-to="Rick">) vous devez marquer la classe avec le code suivant :

[HtmlTargetElement("email", TagStructure = TagStructure.WithoutEndTag)] 
public class EmailVoidTagHelper : TagHelper
{
    private const string EmailDomain = "contoso.com";
    // Code removed for brevity

Avec un Tag Helper e-mail de fermeture automatique, la sortie serait <a href="mailto:Rick@contoso.com" />. Comme les balises d’ancrage de fermeture automatique ne sont pas du code HTML valide, vous n’allez pas en créer, mais vous pouvez peut-être créer un Tag Helper qui se ferme automatiquement. Les Tag Helpers définissent le type de la propriété TagMode après la lecture d’une balise.

Vous pouvez également mapper un autre nom d’attribut à une propriété au moyen de l’attribut[HtmlAttributeName].

Pour mapper un attribut nommé recipient à la propriété MailTo :

[HtmlAttributeName("recipient")]
public string? MailTo { get; set; }

Tag Helper pour l’attribut recipient :

<email recipient="…"/>

ProcessAsync

Dans cette section, nous allons écrire un Tag Helper e-mail asynchrone.

  1. Remplacez la classe EmailTagHelper par le code suivant :

    public class EmailTagHelper : TagHelper
    {
        private const string EmailDomain = "contoso.com";
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName = "a";                                 // Replaces <email> with <a> tag
            var content = await output.GetChildContentAsync();
            var target = content.GetContent() + "@" + EmailDomain;
            output.Attributes.SetAttribute("href", "mailto:" + target);
            output.Content.SetContent(target);
        }
    }
    

    Remarques :

    • Cette version utilise la méthode ProcessAsync asynchrone. L’élément GetChildContentAsync asynchrone retourne un élément Task contenant TagHelperContent.

    • Utilisez le paramètre output pour obtenir le contenu de l’élément HTML.

  2. Apportez la modification suivante au fichier Views/Home/Contact.cshtml pour que le Tag Helper puisse obtenir l’e-mail cible.

    @{
        ViewData["Title"] = "Contact";
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <address>
        One Microsoft Way<br />
        Redmond, WA 98052<br />
        <abbr title="Phone">P:</abbr>
        425.555.0100
    </address>
    
    <address>
        <strong>Support:</strong><email>Support</email><br />
        <strong>Marketing:</strong><email>Marketing</email>
    </address>
    
  3. Exécutez l’application et vérifiez qu’elle génère des liens e-mail valides.

RemoveAll, PreContent.SetHtmlContent et PostContent.SetHtmlContent

  1. Ajoutez la classe BoldTagHelper suivante au dossier TagHelpers.

    using Microsoft.AspNetCore.Razor.TagHelpers;
    
    namespace AuthoringTagHelpers.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>");
            }
        }
    }
    
    • L’attribut [HtmlTargetElement] passe un paramètre d’attribut qui spécifie qu’un élément HTML qui contient un attribut HTML nommé « bold » correspond, et la méthode override Process dans la classe s’exécute. Dans notre exemple, la méthode Process supprime l’attribut « bold » et entoure le balisage contenant avec <strong></strong>.

    • Comme vous ne voulez pas remplacer le contenu de la balise, vous devez écrire la balise <strong> d’ouverture avec la méthode PreContent.SetHtmlContent et la balise </strong> de fermeture avec la méthode PostContent.SetHtmlContent.

  2. Modifiez la vue About.cshtml pour qu’elle contienne une valeur d’attribut bold. Le code terminé est indiqué ci-dessous.

    @{
        ViewData["Title"] = "About";
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <p bold>Use this area to provide additional information.</p>
    
    <bold> Is this bold?</bold>
    
  3. Exécutez l'application. Vous pouvez utiliser votre navigateur favori pour inspecter la source et vérifier le balisage.

    L’attribut [HtmlTargetElement] ci-dessus cible uniquement le balisage HTML qui fournit le nom d’attribut « bold ». L’élément <bold> n’a pas été modifié par le Tag Helper.

  4. Commentez la ligne de l’attribut [HtmlTargetElement] et il ciblera par défaut les balises <bold>, autrement dit le balisage HTML sous la forme <bold>. Gardez à l’esprit que la convention de nommage par défaut associe le nom de classe BoldTagHelper aux balises <bold>.

  5. Exécutez l’application et vérifiez que la balise <bold> est traitée par le Tag Helper.

Le fait de décorer une classe avec plusieurs attributs [HtmlTargetElement] aboutit à une opération OR logique des cibles. Par exemple, avec le code ci-dessous, une balise en gras ou un attribut gras correspondent.

[HtmlTargetElement("bold")]
[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>");
    }
}

Quand plusieurs attributs sont ajoutés à la même instruction, le runtime les traite comme une opération AND logique. Par exemple, dans le code ci-dessous, un élément HTML doit être nommé « bold » avec un attribut nommé « bold » (<bold bold />) pour la mise en correspondance.

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

Vous pouvez également utiliser l’attribut [HtmlTargetElement] pour modifier le nom de l’élément ciblé. Par exemple, si vous souhaitez que BoldTagHelper cible les balises <MyBold>, vous utilisez l’attribut suivant :

[HtmlTargetElement("MyBold")]

Passer un modèle à un Tag Helper

  1. Ajoutez un dossier Models.

  2. Ajoutez la classe WebsiteContext suivante au dossier Models :

    using System;
    
    namespace AuthoringTagHelpers.Models
    {
        public class WebsiteContext
        {
            public Version Version { get; set; }
            public int CopyrightYear { get; set; }
            public bool Approved { get; set; }
            public int TagsToShow { get; set; }
        }
    }
    
  3. Ajoutez la classe WebsiteInformationTagHelper suivante au dossier TagHelpers.

    using System;
    using AuthoringTagHelpers.Models;
    using Microsoft.AspNetCore.Razor.TagHelpers;
    
    namespace AuthoringTagHelpers.TagHelpers
    {
        public class WebsiteInformationTagHelper : TagHelper
        {
            public WebsiteContext Info { get; set; }
    
          public override void Process(TagHelperContext context, TagHelperOutput output)
          {
             output.TagName = "section";
             output.Content.SetHtmlContent(
    $@"<ul><li><strong>Version:</strong> {Info.Version}</li>
    <li><strong>Copyright Year:</strong> {Info.CopyrightYear}</li>
    <li><strong>Approved:</strong> {Info.Approved}</li>
    <li><strong>Number of tags to show:</strong> {Info.TagsToShow}</li></ul>");
             output.TagMode = TagMode.StartTagAndEndTag;
          }
       }
    }
    
    • Comme mentionné précédemment, les Tag Helpers convertissent les propriétés et noms de classe C# de casse Pascal en casse kebab (mots séparés par des tirets). Par conséquent, pour utiliser la classe WebsiteInformationTagHelper dans Razor, vous allez écrire <website-information />.

    • Comme vous n’identifiez pas de manière explicite l’élément cible avec l’attribut [HtmlTargetElement], la valeur par défaut de website-information est ciblée. Si vous avez appliqué l’attribut suivant (notez que la casse n’est pas kebab, mais il correspond au nom de la classe) :

    [HtmlTargetElement("WebsiteInformation")]
    

    La balise en casse kebab <website-information /> ne correspond pas. Si vous voulez utiliser l’attribut [HtmlTargetElement], vous employez la casse kebab comme indiqué ci-dessous :

    [HtmlTargetElement("Website-Information")]
    
    • Les éléments de fermeture automatique n’ont aucun contenu. Pour cet exemple, le balisage Razor utilise une balise de fermeture automatique, mais le Tag Helper crée un élément section (qui ne se ferme pas automatiquement et vous écrivez du contenu dans l’élément section). Par conséquent, vous devez affecter à TagMode la valeur StartTagAndEndTag pour écrire la sortie. Sinon, vous pouvez commenter le paramètre de ligne TagMode et écrire le balisage avec une balise de fermeture. (Un exemple de balisage est fourni plus loin dans ce didacticiel.)

    • Le signe $ (signe dollar) de la ligne suivante utilise une chaîne interpolée :

    $@"<ul><li><strong>Version:</strong> {Info.Version}</li>
    
  4. Ajoutez le balisage suivant à l’affichage About.cshtml. Le balisage en surbrillance affiche les informations de site web.

    @using AuthoringTagHelpers.Models
    @{
        ViewData["Title"] = "About";
        WebsiteContext webContext = new WebsiteContext {
                                        Version = new Version(1, 3),
                                        CopyrightYear = 1638,
                                        Approved = true,
                                        TagsToShow = 131 };
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <p bold>Use this area to provide additional information.</p>
    
    <bold> Is this bold?</bold>
    
    <h3> web site info </h3>
    <website-information info="webContext" />
    

    Notes

    Dans le balisage Razor indiqué ci-dessous :

    <website-information info="webContext" />
    

    Razor identifie que l’attribut info est une classe, pas une chaîne, et que vous souhaitez écrire du code C#. N’importe quel attribut de Tag Helper autre qu’une chaîne doit être écrit sans le caractère @.

  5. Exécutez l’application et accédez à la vue About (À propos de) pour afficher les informations de site web.

    Notes

    Vous pouvez utiliser le balisage suivant avec une balise de fermeture et supprimer la ligne avec TagMode.StartTagAndEndTag dans le Tag Helper :

    <website-information info="webContext" >
    </website-information>
    

Tag Helper Condition

Le Tag Helper Condition restitue la sortie quand une valeur true lui est transmise.

  1. Ajoutez la classe ConditionTagHelper suivante au dossier TagHelpers.

    using Microsoft.AspNetCore.Razor.TagHelpers;
    
    namespace AuthoringTagHelpers.TagHelpers
    {
        [HtmlTargetElement(Attributes = nameof(Condition))]
        public class ConditionTagHelper : TagHelper
        {
            public bool Condition { get; set; }
    
            public override void Process(TagHelperContext context, TagHelperOutput output)
            {
                if (!Condition)
                {
                    output.SuppressOutput();
                }
            }
        }
    }
    
  2. Remplacez le contenu du fichier Views/Home/Index.cshtml par le balisage suivant :

    @using AuthoringTagHelpers.Models
    @model WebsiteContext
    
    @{
        ViewData["Title"] = "Home Page";
    }
    
    <div>
        <h3>Information about our website (outdated):</h3>
        <Website-InforMation info="Model" />
        <div condition="Model.Approved">
            <p>
                This website has <strong surround="em">@Model.Approved</strong> been approved yet.
                Visit www.contoso.com for more information.
            </p>
        </div>
    </div>
    
  3. Remplacez la méthode Index dans le contrôleur Home par le code suivant :

    public IActionResult Index(bool approved = false)
    {
        return View(new WebsiteContext
        {
            Approved = approved,
            CopyrightYear = 2015,
            Version = new Version(1, 3, 3, 7),
            TagsToShow = 20
        });
    }
    
  4. Exécutez l’application et accédez à la page d’accueil. Le balisage de l’élément div conditionnel ne sera pas rendu. Ajoutez la chaîne de requête ?approved=true à l’URL (par exemple, http://localhost:1235/Home/Index?approved=true). approved a la valeur true et le balisage conditionnel s’affichera.

Notes

Utilisez l’opérateur nameof pour spécifier l’attribut à cibler plutôt qu’une chaîne comme vous le faisiez avec le Tag Helper Bold :

[HtmlTargetElement(Attributes = nameof(Condition))]
 //   [HtmlTargetElement(Attributes = "condition")]
 public class ConditionTagHelper : TagHelper
{
   public bool Condition { get; set; }

   public override void Process(TagHelperContext context, TagHelperOutput output)
   {
      if (!Condition)
      {
         output.SuppressOutput();
      }
   }
}

L’opérateur nameof protégera le code s’il devait jamais être refactorisé (le nom peut être changé en RedCondition).

Éviter les conflits de Tag Helpers

Dans cette section, vous écrivez une paire de Tag Helpers de liaison automatique. Le premier remplace le balisage contenant une URL commençant par HTTP par une balise d’ancrage HTML contenant la même URL (et produisant ainsi un lien vers l’URL). Le second effectue la même opération pour une URL commençant par WWW.

Comme ces deux Tag Helpers sont étroitement liés et que vous pouvez les refactoriser à l’avenir, nous allons les conserver dans le même fichier.

  1. Ajoutez la classe AutoLinkerHttpTagHelper suivante au dossier TagHelpers.

    [HtmlTargetElement("p")]
    public class AutoLinkerHttpTagHelper : TagHelper
    {
        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var childContent = await output.GetChildContentAsync();
            // Find Urls in the content and replace them with their anchor tag equivalent.
            output.Content.SetHtmlContent(Regex.Replace(
                 childContent.GetContent(),
                 @"\b(?:https?://)(\S+)\b",
                  "<a target=\"_blank\" href=\"$0\">$0</a>"));  // http link version}
        }
    }
    

    Notes

    La classe AutoLinkerHttpTagHelper cible les éléments p et utilise Regex pour créer l’ancre.

  2. Ajoutez le balisage suivant à la fin du fichier Views/Home/Contact.cshtml :

    @{
        ViewData["Title"] = "Contact";
    }
    <h2>@ViewData["Title"].</h2>
    <h3>@ViewData["Message"]</h3>
    
    <address>
        One Microsoft Way<br />
        Redmond, WA 98052<br />
        <abbr title="Phone">P:</abbr>
        425.555.0100
    </address>
    
    <address>
        <strong>Support:</strong><email>Support</email><br />
        <strong>Marketing:</strong><email>Marketing</email>
    </address>
    
    <p>Visit us at http://docs.asp.net or at www.microsoft.com</p>
    
  3. Exécutez l’application et vérifiez que le Tag Helper restitue correctement l’ancre.

  4. Mettez à jour la classe AutoLinker pour inclure AutoLinkerWwwTagHelper qui convertira le texte www en une balise d’ancrage qui contient également le texte www d’origine. Le code mis à jour est en surbrillance ci-dessous :

        [HtmlTargetElement("p")]
        public class AutoLinkerHttpTagHelper : TagHelper
        {
            public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
            {
                var childContent = await output.GetChildContentAsync();
                // Find Urls in the content and replace them with their anchor tag equivalent.
                output.Content.SetHtmlContent(Regex.Replace(
                     childContent.GetContent(),
                     @"\b(?:https?://)(\S+)\b",
                      "<a target=\"_blank\" href=\"$0\">$0</a>"));  // http link version}
            }
        }
    
        [HtmlTargetElement("p")]
        public class AutoLinkerWwwTagHelper : TagHelper
        {
            public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
            {
                var childContent = await output.GetChildContentAsync();
                // Find Urls in the content and replace them with their anchor tag equivalent.
                output.Content.SetHtmlContent(Regex.Replace(
                    childContent.GetContent(),
                     @"\b(www\.)(\S+)\b",
                     "<a target=\"_blank\" href=\"http://$0\">$0</a>"));  // www version
            }
        }
    }
    
  5. Exécutez l'application. Notez que le texte www est affiché sous forme de lien, contrairement au texte HTTP. Si vous placez un point d’arrêt dans les deux classes, vous pouvez voir que la classe du Tag Helper HTTP s’exécute en premier. Le problème est que la sortie du Tag Helper est mise en cache et, quand le Tag Helper WWW est exécuté, il remplace la sortie mise en cache du Tag Helper HTTP. Plus loin dans ce didacticiel, nous verrons comment contrôler l’ordre d’exécution des Tag Helpers. Nous allons corriger le code avec les éléments suivants :

      public class AutoLinkerHttpTagHelper : TagHelper
      {
          public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
          {
              var childContent = output.Content.IsModified ? output.Content.GetContent() :
                  (await output.GetChildContentAsync()).GetContent();
    
              // Find Urls in the content and replace them with their anchor tag equivalent.
              output.Content.SetHtmlContent(Regex.Replace(
                   childContent,
                   @"\b(?:https?://)(\S+)\b",
                    "<a target=\"_blank\" href=\"$0\">$0</a>"));  // http link version}
          }
      }
    
      [HtmlTargetElement("p")]
      public class AutoLinkerWwwTagHelper : TagHelper
      {
          public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
          {
              var childContent = output.Content.IsModified ? output.Content.GetContent() : 
                  (await output.GetChildContentAsync()).GetContent();
    
              // Find Urls in the content and replace them with their anchor tag equivalent.
              output.Content.SetHtmlContent(Regex.Replace(
                   childContent,
                   @"\b(www\.)(\S+)\b",
                   "<a target=\"_blank\" href=\"http://$0\">$0</a>"));  // www version
          }
      }
    

    Notes

    Dans la première édition des Tag Helpers de liaison automatique, vous avez obtenu le contenu de la cible avec le code suivant :

    var childContent = await output.GetChildContentAsync();
    

    Autrement dit, vous appelez GetChildContentAsync à l’aide de l’élément TagHelperOutput passé dans la méthode ProcessAsync. Comme mentionné précédemment, étant donné que la sortie est mise en cache, le dernier Tag Helper qui est exécuté l’emporte. Vous avez résolu ce problème avec le code suivant :

    var childContent = output.Content.IsModified ? output.Content.GetContent() : 
        (await output.GetChildContentAsync()).GetContent();
    

    Le code ci-dessus vérifie si le contenu a été modifié et, le cas échéant, obtient le contenu à partir de la mémoire tampon de sortie.

  6. Exécutez l’application et vérifiez que les deux liens fonctionnent comme prévu. Alors qu’il peut sembler que notre Tag Helper de liaison automatique est correct et terminé, il pose un problème subtil. Si le Tag Helper WWW est exécuté en premier, les liens www ne sont pas corrects. Mettez à jour le code en ajoutant la surcharge Order pour contrôler l’ordre d’exécution des Tag Helpers. La propriété Order détermine l’ordre d’exécution par rapport à d’autres Tag Helpers ciblant le même élément. La valeur d’ordre par défaut est égale à zéro et les instances ayant des valeurs inférieures sont exécutées en premier.

    public class AutoLinkerHttpTagHelper : TagHelper
    {
        // This filter must run before the AutoLinkerWwwTagHelper as it searches and replaces http and 
        // the AutoLinkerWwwTagHelper adds http to the markup.
        public override int Order
        {
            get  {  return int.MinValue;   }
        }
    

    Le code précédent garantit que le Tag Helper HTTP s’exécute avant le Tag Helper WWW. Remplacez Order par MaxValue et vérifiez que le balisage généré pour la balise WWW est incorrect.

Examiner et récupérer le contenu enfant

Les Tag Helpers fournissent plusieurs propriétés permettant de récupérer le contenu.

  • Le résultat de GetChildContentAsync peut être ajouté à output.Content.
  • Vous pouvez examiner le résultat de GetChildContentAsync avec GetContent.
  • Si vous modifiez output.Content, le corps TagHelper n’est pas exécuté ni restitué, sauf si vous appelez GetChildContentAsync comme dans notre exemple de liaison automatique :
public class AutoLinkerHttpTagHelper : TagHelper
{
    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        var childContent = output.Content.IsModified ? output.Content.GetContent() :
            (await output.GetChildContentAsync()).GetContent();

        // Find Urls in the content and replace them with their anchor tag equivalent.
        output.Content.SetHtmlContent(Regex.Replace(
             childContent,
             @"\b(?:https?://)(\S+)\b",
              "<a target=\"_blank\" href=\"$0\">$0</a>"));  // http link version}
    }
}
  • Plusieurs appels à GetChildContentAsync retournent la même valeur et ne réexécutent pas le corps TagHelper, sauf si vous passez un paramètre false indiquant de ne pas utiliser le résultat mis en cache.

Charger TagHelper avec une vue partielle minimisée

Dans les environnements de production, les performances peuvent être améliorées en chargeant des vues partielles minimisées. Pour tirer parti de la vue partielle minimisée en production :

  • Créez/configurez un processus pré-build qui minimise les vues partielles.
  • Utilisez le code suivant pour charger des vues partielles minimisées dans des environnements non liés au développement.
public class MinifiedVersionPartialTagHelper : PartialTagHelper
    {
        public MinifiedVersionPartialTagHelper(ICompositeViewEngine viewEngine, 
                                IViewBufferScope viewBufferScope)
                               : base(viewEngine, viewBufferScope)
        {

        }

        public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            // Append ".min" to load the minified partial view.
            if (!IsDevelopment())
            {
                Name += ".min";
            }

            return base.ProcessAsync(context, output);
        }

        private bool IsDevelopment()
        {
            return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") 
                                                 == EnvironmentName.Development;
        }
    }