Introduzione ai Tag Helper personalizzati in ASP.NET MVC

ASP.NET Core MVC fornisce ai tag helper un potente meccanismo lato server per trasformare e generare markup HTML direttamente all'interno delle viste Razor. Mentre le navi framework con un ricco set di tag integrato per forme, validazione, cache e progetti di ambiente-specifici spesso richiedono componenti personalizzati che incapsulano i modelli di configurazione specifica del dominio e la presentazione personalizzata.

Trasferindo la generazione HTML complessa e la logica condizionale in classi di helper tag riutilizzabili, si separano le preoccupazioni di presentazione dal view markup, migliorano la testabilità e riducono la duplicazione attraverso grandi codebases. Questo articolo fornisce una guida completa per la costruzione di tag helper personalizzati in ASP.NET Core MVC, coprendo tutto dalla struttura di base della classe e attributo vincolante per scenari avanzati come iniezione di dipendenza, elaborazione asincrono e componenti di aiuto di tag.

Capire i Tag Helpers in ASP.NET Core

I tag helper sono componenti lato server che partecipano alla rendering di elementi HTML nelle viste Razor. Essi consentono di collegare il codice C# a specifici elementi HTML o nomi di elementi personalizzati, trasformando il markup prima che venga inviato al client.

Ad esempio, il tag helper integrato unisce il controller e gli attributi di azione nell'URL corretto. I tag helper possono modificare gli attributi, sostituire l'intero elemento, aggiungere o rimuovere le classi CSS, e anche iniettare l'HTML aggiuntivo.

Modello di esecuzione del programma server

Quando viene compilata una visualizzazione Razor, i tag helper vengono scoperti attraverso le istruzioni . Il framework valuta ogni elemento HTML contro tutti gli helper registrati, invocando i loro [] o metodi quando si trova una partita. Questo accade prima che l'output HTML finale venga generato, permettendo ai tag helper di arricchire o sostituire gli elementi dinamicamente.

Differenze chiave da altri strumenti di composizione di vista

  • HML Helpers:[] Richiedere chiamate C# all'interno dei blocchi Razor ([[]]), che possono rompere il flusso HTML-centrico.
  • Partials:[] Buon per riutilizzare i pezzi statici di markup ma non è possibile modificare programmaticamente la struttura in base alla logica del server senza ulteriori modelli di visualizzazione.
  • Visualizza componenti:[]] Ideale per widget complessi e basati su dati con la loro logica e rendering, ma richiedono una sintassi di classe e invocazione separata ().

Perché creare Personale Tag Helpers?

Mentre i tag helper incorporati coprono molti scenari comuni, i tag helper personalizzati offrono vantaggi unici che migliorano direttamente la qualità del codice e la produttività dello sviluppatore.

  • Imcapsulate Complex Markup Patterns[[] – Il ripetizione del bollitore HTML (come componenti della scheda strutturata, tabelle dei dati o pulsanti in stile) può essere incapsulato in un unico elemento personalizzato.
  • Consistency di progettazione di forza[[] – Un utente personalizzato [[] o []] tag helper può far rispettare classi CSS coerenti, attributi di accessibilità e modelli di data-binding, riducendo la possibilità di incongruenze UI.
  • Migliora la leggibilità della vista[[] – Invece di nidificati [] e blocchi C# condizionali, un singolo tag con pochi attributi è molto più facile da scansionare e mantenere.
  • Attiva test unità di vista Logic[] – Poiché i tag helper sono classi semplici che producono HTML, è possibile scrivere test unità per verificare l'output generato per vari input, qualcosa di difficile da raggiungere con il codice Razor in linea.
  • Aumentare la riutilizzabilità tra i progetti[[]] – Una libreria di helper per tag personalizzati può essere confezionata come componente NuGet e condivisa in più soluzioni, promuovendo un toolkit UI coerente.

Creazione di un aiuto di base per tag personalizzato

Ogni tag helper personalizzato eredita dalla classe ] (o implementa direttamente ) ed è decorato con l'attributo per specificare quale elemento HTML o attributo esso si rivolge. Il nucleo della logica vive all'interno del metodo (o la sua controparte asincrona ).

Passo 1: Definire la classe Tag Helper

Creare una nuova classe C# nel tuo progetto, tipicamente all'interno di una cartella .Erezione da [ e applicare l'attributo con il nome dell'elemento che si intende utilizzare nelle tue opinioni.

[HtmlTargetElement("custom-card")]
public class CustomCardTagHelper : TagHelper
{
 public string Title { get; set; }
 public string CssClass { get; set; } = "card-default";

 public override void Process(TagHelperContext context, TagHelperOutput output)
 {
 // Replace the custom tag with a div and add the desired structure
 output.TagName = "div";
 output.Attributes.SetAttribute("class", $"card {CssClass}");

 // Build inner content
 output.Content.SetHtmlContent(
 $@"<div class=""card-header"">{Title}</div>
 <div class=""card-body"">
 {output.Content.GetContent()}
 </div>"
 );
 }
}

Passo 2: Comprendere TagHelperContext e TagHelperOutput

] fornisce informazioni sull'elemento corrente e sui suoi attributi.[]] consente di modificare il nome, gli attributi e i contenuti dell'elemento. È inoltre possibile utilizzare [ per testo normale o per l'HTML raw. Per preservare il contenuto originale (ad esempio, elementi per bambini all'interno del tag personalizzato), si chiama

Passo 3: Registrare il Tag Helper

Se i vostri helper di tag risiedono in una libreria di classe separata, è necessario aggiungere una direttiva in :

@addTagHelper *, MyApp.TagHelpers

Il formato è o una wildcard con []] per includere tutti i tag helper da quell'assemblea. È inoltre possibile utilizzare per escludere specifici helper.

Passo 4: Utilizzare il Tag Helper in una vista Razor

Con la registrazione in atto, è possibile utilizzare l'elemento personalizzato:

<custom-card title="Welcome" css-class="card-primary">
 This is the body content
</custom-card>

Questo produce:

<div class="card card-primary">
 <div class="card-header">Welcome</div>
 <div class="card-body">
 This is the body content
 </div>
</div>

Tecniche di aiuto per tag avanzate

Utilizzo di attributi e proprietà incatenazione

Per esempio, un tag helper che rende un input di forma può legare una proprietà ad un'espressione del modello:

[HtmlTargetElement("email-input")]
public class EmailInputTagHelper : TagHelper
{
 [HtmlAttributeName("asp-for")]
 public ModelExpression For { get; set; }

 public override void Process(TagHelperContext context, TagHelperOutput output)
 {
 output.TagName = "input";
 output.Attributes.SetAttribute("type", "email");
 output.Attributes.SetAttribute("id", For.Name);
 output.Attributes.SetAttribute("name", For.Name);
 output.Attributes.SetAttribute("value", For.Model?.ToString() ?? "");
 }
}

L'attributo mappa la proprietà C# a un nome di attributo HTML specifico. Utilizzando , si accede ai metadati del modello per una completa integrazione con la logica di convalida e visualizzazione.

Lavorazione asincrona

Se il tuo helper del tag ha bisogno di eseguire operazioni I/O (ad esempio, il recupero dei dati da un database), sovrascrivere invece:

public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
 var data = await _someService.GetDataAsync();
 output.Content.SetHtmlContent(data);
}

Iniezione di dipendenza nel Tag Helpers

Tag helper supporto costruttore iniezione come qualsiasi altro servizio MVC. Basta aggiungere la vostra dipendenza al costruttore, e il contenitore DI risolverà:

public class UserProfileTagHelper : TagHelper
{
 private readonly IUserService _userService;

 public UserProfileTagHelper(IUserService userService)
 {
 _userService = userService;
 }

 public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
 {
 var user = await _userService.GetCurrentUserAsync();
 // render user profile markup
 }
}

Nota che i tag helper sono transitori per impostazione predefinita; viene creata una nuova istanza per ogni utilizzo in una vista.

Tag Componenti per iniezione HTML globale

Introdotto in ASP.NET Core 2.1, ]Tag Helper Components[]] ti permette di iniettare markup in ogni risposta a livello globale, tipicamente utilizzato per bundling script, stili o analisi.

public class GlobalScriptTagHelperComponent : TagHelperComponent
{
 public override void Process(TagHelperContext context, TagHelperOutput output)
 {
 if (output.TagName == "body" && output.Attributes.ContainsName("data-scripts"))
 {
 output.PostContent.AppendHtml("<script src='/js/global.js'></script>");
 }
 }
}

// In Startup.ConfigureServices
services.AddTransient<ITagHelperComponent, GlobalScriptTagHelperComponent>();

Esempi pratici per la visualizzazione della composizione

1. Condizionatore di Wrapper Tag Helper

Avvolgi il contenuto con un elemento aggiuntivo solo se viene soddisfatta una condizione, utile per i contenitori di layout reattivi:

[HtmlTargetElement("if-wrapper")]
public class IfWrapperTagHelper : TagHelper
{
 public bool Condition { get; set; }

 public override void Process(TagHelperContext context, TagHelperOutput output)
 {
 if (!Condition)
 {
 // Remove the wrapping element, output only the child content
 output.TagName = null;
 output.Content.SetContent(output.Content.GetContent());
 }
 else
 {
 output.TagName = "div";
 output.Attributes.SetAttribute("class", "wrapper");
 }
 }
}

2. Immagine con carico pigro e setti

Creare un tag helper che genera tag reattivi con attributo e sorgenti multiple:

[HtmlTargetElement("lazy-image")]
public class LazyImageTagHelper : TagHelper
{
 public string Src { get; set; }
 public string Srcset { get; set; }
 public string Alt { get; set; }
 public string CssClass { get; set; }

 public override void Process(TagHelperContext context, TagHelperOutput output)
 {
 output.TagName = "img";
 output.Attributes.SetAttribute("src", Src);
 if (!string.IsNullOrEmpty(Srcset))
 output.Attributes.SetAttribute("srcset", Srcset);
 output.Attributes.SetAttribute("alt", Alt);
 output.Attributes.SetAttribute("loading", "lazy");
 if (!string.IsNullOrEmpty(CssClass))
 output.Attributes.SetAttribute("class", CssClass);
 }
}

3. Riepilogo di convalida con struttura personalizzata

Invece di utilizzare il tag di convalida incorporato, crea uno che aggiunge icone e styling personalizzati:

[HtmlTargetElement("custom-validation-summary")]
public class CustomValidationSummaryTagHelper : TagHelper
{
 [HtmlAttributeName("asp-validation-summary")]
 public ValidationSummary ValidationSummary { get; set; }

 [ViewContext]
 public ViewContext ViewContext { get; set; }

 public override void Process(TagHelperContext context, TagHelperOutput output)
 {
 var viewData = ViewContext.ViewData;
 var errors = viewData.ModelState.Where(s => s.Value.Errors.Count > 0).SelectMany(s => s.Value.Errors).ToList();

 if (errors.Count == 0)
 {
 output.SuppressOutput();
 return;
 }

 output.TagName = "div";
 output.Attributes.SetAttribute("class", "alert alert-danger");
 var list = new StringBuilder();
 list.Append("<ul class='mb-0'>");
 foreach (var error in errors)
 {
 list.Append($"<li><strong>Error:</strong> {error.ErrorMessage}</li>");
 }
 list.Append("</ul>");
 output.Content.SetHtmlContent(list.ToString());
 }
}

Migliori Pratiche per la costruzione Tag Helpers

  • Tenere sotto controllo la logica C# minimal[[] – I Tag helper sono per la trasformazione della presentazione, non per la logica aziendale.
  • Favor [] per I/O] – Anche se la tua attuale implementazione è sincrona, usando rende più facile aggiungere chiamate asincroni in seguito senza interrompere i cambiamenti.
  • Usa nomi descrittivi per elementi personalizzati[[] – Seguire una convenzione come ] o [] per evitare collisioni con futuri standard HTML.
  • Leverage [ – Per le proprietà interne che non devono essere impostate dal markup, contrassegnate con questo attributo.
  • Test the generate HTML[] – Scrivere test unità che istantano il tag helper, invoca , e verificare l'output utilizzando affermazioni.
  • Consider accessibilità[] – Aggiungi attributi ARIA e supporto tastiera, se del caso.

Pitfalls e risoluzione dei problemi

  • Il aiutante di Tag non viene scoperto[[] – Assicurare la direttiva [] in [ punti al montaggio corretto.
  • Attribuzione nomi non corrispondenti[] – Utilizzare l'attributo per mappare i nomi di proprietà C# ai nomi di attributo HTML.
  • Non è necessario rendere[[] – Se si chiama prima di recuperare il contenuto del bambino, si perde l'HTML interno originale.
  • I helper di tag multiplo che mirano allo stesso elemento[ – L'ordine di esecuzione segue l'ordine alfabetico per impostazione predefinita, ma possono essere controllati con la proprietà .
  • Problemi di iniezione di dipendenza[[[] – I helper tag non sono singleton perseguiti. Se si inietta un servizio di portata, assicurarsi che l'helper tag viene consumato all'interno dello stesso campo di richiesta HTTP (di solito lo è).

Integrare Personalizzato Tag Helpers in un codice esistente

Adottando i tag helper personalizzati non richiede una riscrittura completa. Puoi iniziare rifacendo i modelli più ripetitivi – come pulsanti, schede o tabelle di dati – in tag helper. Col tempo, costruirai una libreria che diventa la sola fonte di verità per i tuoi componenti UI. Combina i tag helper con altri strumenti di composizione come parziali e visualizza componenti per la massima flessibilità.

Risorse esterne e lettura

Conclusioni

I tag helper personalizzati rappresentano una significativa evoluzione nel modo in cui gli sviluppatori compongono le visualizzazioni in ASP.NET Core MVC. Permettendo di definire i propri elementi HTML e attributi che eseguono la logica del server, colmano il divario tra markup e programmatore-controllo. Dai semplici generatori di tasti ai componenti complessi e di stato-aware che si integrano con la tecnica di iniezione di dipendenza, tag helper che ti permettono di costruire interfacce utente manutenbili, testable e coerenti.