Creare helper tag in ASP.NET Core

Di Rick Anderson

Visualizzare o scaricare il codice di esempio (procedura per il download)

Guida introduttiva agli helper tag

Questa esercitazione rappresenta un'introduzione alla programmazione di helper tag. Introduzione agli helper tag descrive i vantaggi offerti dagli helper tag.

Un helper tag è una classe che implementa l'interfaccia ITagHelper. Quando si crea un helper tag, tuttavia, in genere lo si deriva da TagHelper. In questo modo si ottiene l'accesso al metodo Process.

  1. Creare un nuovo progetto ASP.NET Core denominato AuthoringTagHelpers. Per questo progetto non sarà necessaria l'autenticazione.

  2. Creare una cartella che contenga gli helper tag denominata TagHelpers. La cartella TagHelpersnon è obbligatoria, ma rappresenta una convenzione sensata. È ora possibile iniziare a scrivere alcuni helper tag semplici.

Un helper tag piccolissimo

In questa sezione verrà scritto un helper tag che aggiorna un tag di posta elettronica. Ad esempio:

<email>Support</email>

Il server userà l'helper tag di posta elettronica creato per convertire il markup nel codice seguente:

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

In altre parole, un tag di ancoraggio che dà come risultato un collegamento di posta elettronica. È necessario eseguire questa operazione se si sta scrivendo un motore di blog e si vuole inviare posta elettronica di marketing o di supporto ad altri contatti, tutti nello stesso dominio.

  1. Aggiungere la classe EmailTagHelper seguente alla cartella 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
            }
        }
    }
    
    • Gli helper tag usano una convenzione di denominazione che interessa gli elementi del nome della classe di base (meno la parte TagHelper del nome della classe). In questo esempio il nome radice di EmailTagHelper è email, pertanto il tag <email> viene considerato come destinazione. Questa convenzione di denominazione dovrebbe funzionare per la maggior parte degli helper tag. Più avanti verrà illustrato come eseguirne l'override.

    • La classe EmailTagHelper deriva da TagHelper. La classe TagHelper mette a disposizione metodi e proprietà per la scrittura di helper tag.

    • Il metodo Process sottoposto a override controlla ciò che l'helper tag fa quando viene eseguito. La classe TagHelper fornisce anche una versione asincrona (ProcessAsync) con gli stessi parametri.

    • Il parametro context di Process (e ProcessAsync) contiene informazioni associate all'esecuzione del tag HTML corrente.

    • Il parametro output di Process (e ProcessAsync) contiene un elemento HTML con stato rappresentativo dell'origine originale usato per generare un tag e del contenuto HTML.

    • Il nome della classe usata ha il suffisso TagHelper, che non è obbligatorio, ma il cui uso è considerato una convenzione basata su procedure consigliate. È possibile dichiarare la classe come:

    public class Email : TagHelper
    
  2. Per rendere la EmailTagHelper classe disponibile per tutte le Razor visualizzazioni, aggiungere la addTagHelper direttiva al Views/_ViewImports.cshtml file :

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

    Il codice qui sopra usa la sintassi con caratteri jolly per specificare che tutti gli helper tag in questo assembly saranno disponibili. La prima stringa dopo @addTagHelper specifica l'helper tag da caricare (usare "*" per indicare tutti gli helper tag), e la seconda stringa "AuthoringTagHelpers" specifica l'assembly in cui si trova l'helper tag. Si noti anche che la seconda riga inserisce gli helper tag MVC core ASP.NET usando la sintassi con caratteri jolly (tali helper sono descritti in Introduzione agli helper tag). Si tratta della @addTagHelper direttiva che rende disponibile l'helper tag per la Razor visualizzazione. In alternativa, è possibile specificare il nome completo dell'helper tag, come illustrato di seguito:

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

Per aggiungere un helper tag a una visualizzazione usando un nome completo, aggiungere prima il nome completo (AuthoringTagHelpers.TagHelpers.EmailTagHelper) e quindi il nome dell'assembly (AuthoringTagHelpers, non necessariamente namespace). La maggior parte degli sviluppatori preferisce usare la sintassi con caratteri jolly. Introduzione agli helper tag descrive in dettaglio la sintassi di aggiunta, rimozione, gerarchia degli helper tag, nonché la sintassi con caratteri jolly.

  1. Aggiornare il markup nel Views/Home/Contact.cshtml file con queste modifiche:

    @{
        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. Eseguire l'app e usare il browser preferito per visualizzare l'origine HTML e verificare che i tag di posta elettronica siano stati sostituiti dal markup di ancoraggio, ad esempio, <a>Support</a>. Support e Marketing sono visualizzati come collegamenti, ma non hanno un attributo href che li renda funzionali. Questo problema verrà corretto nella prossima sezione.

SetAttribute e SetContent

In questa sezione la classe EmailTagHelper verrà aggiornata in modo che crei un tag di ancoraggio valido per la posta elettronica. Lo aggiorneremo per acquisire informazioni da una Razor vista (sotto forma di mail-to attributo) e usarle per generare l'ancoraggio.

Aggiornare la classe EmailTagHelper con il codice seguente:

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);
    }
}
  • I nomi della classe e delle proprietà per gli helper tag, scritti secondo la convenzione Pascal, vengono convertiti nel formato kebab case corrispondente. Per usare l'attributo MailTo, pertanto, si userà l'equivalente <email mail-to="value"/>.

  • L'ultima riga imposta il contenuto completato per l'helper tag dalle minime funzioni.

  • La riga evidenziata illustra la sintassi per l'aggiunta di attributi:

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

Questo approccio funziona per l'attributo "href" a condizione che non esista nella raccolta di attributi. È anche possibile usare il metodo output.Attributes.Add per aggiungere un attributo di helper tag alla fine della raccolta di attributi di tag.

  1. Aggiornare il markup nel Views/Home/Contact.cshtml file con queste modifiche:

    @{
        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. Eseguire l'app e verificare che generi collegamenti corretti.

Nota

Se si scrive il tag di posta elettronica a chiusura automatica (<email mail-to="Rick" />), anche l'output finale è a chiusura automatica. Per abilitare la possibilità di scrivere il tag con solo un tag iniziale (<email mail-to="Rick">) è necessario contrassegnare la classe con quanto segue:

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

Con un helper tag di posta elettronica a chiusura automatica, l'output è <a href="mailto:Rick@contoso.com" />. I tag di ancoraggio a chiusura automatica non costituiscono codice HTML valido ed è quindi necessario evitare di crearli, ma può essere necessario creare un helper tag a chiusura automatica. Gli helper tag impostano il tipo della proprietà TagMode dopo la lettura di un tag.

È anche possibile eseguire il mapping di un nome di attributo diverso a una proprietà usando l'attributo [HtmlAttributeName].

Per eseguire il mapping di un attributo denominato recipient alla MailTo proprietà :

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

Helper tag per l'attributo recipient :

<email recipient="…"/>

ProcessAsync

In questa sezione verrà scritto un helper di posta elettronica asincrono.

  1. Sostituire la classe EmailTagHelper con il codice seguente:

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

    Note:

    • Questa versione usa il metodo ProcessAsync asincrono. GetChildContentAsync asincrono restituisce un Task contenente TagHelperContent.

    • Usare il parametro output per ottenere il contenuto dell'elemento HTML.

  2. Apportare la modifica seguente al Views/Home/Contact.cshtml file in modo che l'helper tag possa ottenere il messaggio di posta elettronica di destinazione.

    @{
        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. Eseguire l'app e verificare che generi collegamenti di posta elettronica validi.

RemoveAll, PreContent.SetHtmlContent e PostContent.SetHtmlContent

  1. Aggiungere la classe BoldTagHelper seguente alla cartella 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'attributo [HtmlTargetElement] passa un parametro di attributo che specifica che qualsiasi elemento HTML che contenga un attributo HTML denominato "bold" corrisponderà e che quindi verrà eseguito il metodo di override Process nella classe. Nell'esempio, il metodo Process rimuove l'attributo "bold" e racchiude il markup tra <strong></strong>.

    • Dato che non si deve sostituire il contenuto esistente del tag, è necessario scrivere il tag <strong> di apertura con il metodo PreContent.SetHtmlContent e il tag </strong> di chiusura con il metodo PostContent.SetHtmlContent.

  2. Modificare la About.cshtml visualizzazione in modo che contenga un valore di bold attributo. Il codice completo è illustrato di seguito.

    @{
        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. Eseguire l'app. È possibile usare il browser preferito per controllare l'origine e verificare il markup.

    L'attributo [HtmlTargetElement] qui sopra ha come destinazione solo il markup HTML che fornisce un nome di attributo corrispondente a "bold". L'elemento <bold> non è stato modificato dall'helper tag.

  4. Impostare come commento la riga dell'attributo [HtmlTargetElement]. Per impostazione predefinita, userà quindi tag <bold>, vale a dire il markup HTML nel formato <bold>. Tenere presente che la convenzione di denominazione predefinita corrisponderà al nome della classe BoldTagHelper per i tag <bold>.

  5. Eseguire l'applicazione e verificare che il tag <bold> venga elaborato dall'helper tag.

La decorazione di una classe con più attributi [HtmlTargetElement] risulta in un OR logico delle destinazioni. Usando il codice riportato di seguito, ad esempio, un tag bold o un attributo bold corrisponderanno.

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

Se si aggiungono più attributi alla stessa istruzione, il runtime li gestisce come AND logici. Nel codice riportato di seguito, ad esempio, perché corrisponda, un elemento HTML deve essere denominato "bold" con un attributo denominato "bold" (<bold bold />).

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

È anche possibile usare [HtmlTargetElement] per modificare il nome dell'elemento di destinazione. Se ad esempio si vuole che BoldTagHelper abbia come destinazione tag <MyBold>, usare l'attributo seguente:

[HtmlTargetElement("MyBold")]

Passare un modello a un helper tag

  1. Aggiungere una cartella Models.

  2. Aggiungere la classe WebsiteContext seguente alla cartella 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. Aggiungere la classe WebsiteInformationTagHelper seguente alla cartella 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;
          }
       }
    }
    
    • Come affermato in precedenza, gli helper tag convertono i nomi delle relative classi e proprietà C#, definiti secondo la convenzione Pascal, in formato kebab case. Pertanto, per usare in WebsiteInformationTagHelperRazor, si scriverà <website-information />.

    • Non si sta identificando in modo esplicito l'elemento di destinazione con l'attributo [HtmlTargetElement] e quindi verrà considerato come destinazione il valore predefinito di website-information. Se è stato applicato l'attributo seguente (si noti che non è nel formato kebab case ma corrisponde al nome della classe):

    [HtmlTargetElement("WebsiteInformation")]
    

    Il tag nel formato kebab case <website-information /> non corrisponde. Se si vuole usare l'attributo [HtmlTargetElement], è necessario usare il formato kebab case come illustrato di seguito:

    [HtmlTargetElement("Website-Information")]
    
    • Gli elementi a chiusura automatica non hanno contenuto. Per questo esempio, il Razor markup userà un tag di chiusura automatica, ma l'helper tag creerà un elemento di sezione (che non si chiude automaticamente e si sta scrivendo contenuto all'interno dell'elemento section ). Per scrivere output, è pertanto necessario impostare TagMode su StartTagAndEndTag. In alternativa, è possibile impostare come commento la riga che imposta TagMode e scrivere markup con un tag di chiusura. Del markup di esempio viene fornito più avanti in questa esercitazione.

    • Il simbolo $ (segno di dollaro) nella riga seguente usa una stringa interpolata:

    $@"<ul><li><strong>Version:</strong> {Info.Version}</li>
    
  4. Aggiungere il markup seguente alla About.cshtml visualizzazione. Il markup evidenziato visualizza le informazioni sul sito 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" />
    

    Nota

    Razor Nel markup illustrato di seguito:

    <website-information info="webContext" />
    

    Razor sa che l'attributo info è una classe, non una stringa e si vuole scrivere codice C#. Qualsiasi attributo di helper tag non di tipo stringa deve essere scritto senza il carattere @.

  5. Eseguire l'app e passare alla visualizzazione About per visualizzare le informazioni sul sito Web.

    Nota

    È possibile usare il markup seguente con un tag di chiusura e rimuovere la riga con TagMode.StartTagAndEndTag nell'helper tag:

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

Helper tag di condizione

L'helper tag di condizione esegue il rendering dell'output quando riceve un valore true.

  1. Aggiungere la classe ConditionTagHelper seguente alla cartella 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. Sostituire il contenuto del Views/Home/Index.cshtml file con il markup seguente:

    @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. Sostituire il metodo Index nel controller Home con il codice seguente:

    public IActionResult Index(bool approved = false)
    {
        return View(new WebsiteContext
        {
            Approved = approved,
            CopyrightYear = 2015,
            Version = new Version(1, 3, 3, 7),
            TagsToShow = 20
        });
    }
    
  4. Eseguire l'app e passare alla home page. Non verrà eseguito il rendering del markup in div condizionale. Accodare la stringa di query ?approved=true all'URL (ad esempio, http://localhost:1235/Home/Index?approved=true). approvedè impostato su true e il markup condizionale verrà visualizzato.

Nota

Usare l'operatore nameof per specificare l'attributo di destinazione, anziché specificare una stringa, come è stato fatto per l'helper tag 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'operatore nameof proteggerà il codice nel caso venisse effettuato il refactoring (potrebbe essere necessario modificare il nome in RedCondition).

Evitare conflitti di helper tag

In questa sezione verrà scritta una coppia di helper tag a collegamento automatico. Il primo sostituirà il markup contenente un URL che inizia con HTTP con un tag di ancoraggio HTML contenente lo stesso URL (e che quindi genera un collegamento all'URL). Il secondo eseguirà la stessa operazione per un URL che inizia con WWW.

Poiché questi due helper sono strettamente correlati e in futuro potrebbero essere sottoposti a refactoring, verranno mantenuti nello stesso file.

  1. Aggiungere la classe AutoLinkerHttpTagHelper seguente alla cartella 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}
        }
    }
    

    Nota

    La classe AutoLinkerHttpTagHelper ha come destinazioni elementi p e usa Regex per creare l'ancoraggio.

  2. Aggiungere il markup seguente alla fine del Views/Home/Contact.cshtml file:

    @{
        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. Eseguire l'app e verificare che l'helper tag esegua correttamente il rendering dell'ancoraggio.

  4. Aggiornare la classe AutoLinker per includere AutoLinkerWwwTagHelper che convertirà il testo www in un tag di ancoraggio contenente anch'esso il testo www originale. Il codice aggiornato è evidenziato di seguito:

        [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. Eseguire l'app. Si noti che il testo www è visualizzato come collegamento, ma il testo HTTP non lo è. Se si inserisce un punto di interruzione in entrambe le classi, è possibile notare che la classe helper tag HTTP viene eseguita per prima. Il problema è che l'output dell'helper tag viene memorizzato nella cache. Quando viene eseguito l'helper tag WWW, quest'ultimo sovrascrive l'output dell'helper tag HTTP memorizzato nella cache. Più avanti in questa esercitazione verrà illustrato come controllare l'ordine di esecuzione degli helper tag. È possibile correggere il problema con il codice seguente:

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

    Nota

    Nella prima edizione degli helper tag a collegamento automatico, per ottenere il contenuto della destinazione è stato usato il codice seguente:

    var childContent = await output.GetChildContentAsync();
    

    In altre parole, si effettua una chiamata a GetChildContentAsync usando TagHelperOutput passato al metodo ProcessAsync. Come affermato in precedenza, poiché l'output viene memorizzato nella cache, l'ultimo helper eseguito prevale. Il problema è stato risolto con il codice seguente:

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

    Il codice precedente controlla se il contenuto è stato modificato e, in caso affermativo, ottiene il contenuto dal buffer di output.

  6. Eseguire l'app e verificare che i due collegamenti funzionino come previsto. Può sembrare che l'helper tag a collegamento automatico sia corretto e completo, ma presenta un problema insidioso. Se l'helper tag WWW viene eseguito per primo, i collegamenti www non sono corretti. Aggiornare il codice aggiungendo l'overload Order per controllare l'ordine in cui viene eseguito il tag. La proprietà Order determina l'ordine di esecuzione rispetto agli altri helper tag che hanno come destinazione lo stesso elemento. Il valore dell'ordine predefinito è zero e le istanze con valori inferiori vengono eseguite per prime.

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

    Il codice precedente assicura che l'helper tag HTTP venga eseguito prima dell'helper tag WWW. Modificare Order in MaxValue e verificare che il markup generato per il tag WWW non è corretto.

Controllare e recuperare il contenuto figlio

Gli helper tag mettono a disposizione diverse proprietà per recuperare il contenuto.

  • Il risultato di GetChildContentAsync può essere accodato a output.Content.
  • È possibile controllare il risultato di GetChildContentAsync con GetContent.
  • Se si modifica output.Content, il corpo di TagHelper viene eseguito, o ne viene eseguito il rendering, solo se si chiama GetChildContentAsync, come nell'esempio di helper tag a collegamento automatico:
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}
    }
}
  • Più chiamate al metodo GetChildContentAsync restituiscono lo stesso valore e rieseguono nuovamente il corpo di TagHelper solo se viene passato un parametro false per indicare di non usare il risultato memorizzato nella cache.

TagHelper per caricare visualizzazioni parziali minimizzate

Negli ambienti di produzione le prestazioni possono essere migliorate caricando visualizzazioni parziali minimizzate. Per sfruttare i vantaggi della visualizzazione parziale minimizzata nell'ambiente di produzione:

  • Creare/configurare un processo di pre-compilazione che minimizza visualizzazioni parziali.
  • Usare il codice seguente per caricare le visualizzazioni parziali minimizzate in ambienti non di sviluppo.
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;
        }
    }