Componenti di visualizzazione in ASP.NET Core

Di Rick Anderson

Componenti di visualizzazione

I componenti di visualizzazione hanno aspetti comuni con le visualizzazioni parziali, ma sono molto più efficienti. I componenti di visualizzazione non usano l'associazione di modelli, dipendono dai dati passati quando si chiama il componente di visualizzazione. Questo articolo è stato scritto usando controller e visualizzazioni, ma i componenti di visualizzazione funzionano con Razor Pages.

Un componente di visualizzazione:

  • Esegue il rendering di un blocco anziché di un'intera risposta.
  • Include la stessa separazione dei concetti e gli stessi vantaggi per i test individuati nel controller e nella visualizzazione.
  • Può contenere parametri e logica di business.
  • In genere viene richiamato da una pagina di layout.

I componenti di visualizzazione sono destinati ovunque la logica di rendering riutilizzabile troppo complessa per una visualizzazione parziale, ad esempio:

  • Menu di spostamento dinamici
  • Contrassegna il cloud, in cui esegue query sul database
  • Pannello di accesso
  • Carrello acquisti
  • Articoli recentemente pubblicati
  • Contenuto della barra laterale in un blog
  • Pannello di accesso di cui viene eseguito il rendering in ogni pagina e vengono visualizzati i collegamenti per disconnettersi o accedere, a seconda dello stato di accesso dell'utente

Un componente di visualizzazione è costituito da due parti:

  • Classe, in genere derivata da ViewComponent
  • Risultato restituito, in genere una visualizzazione.

Come i controller, un componente di visualizzazione può essere poco, ma la maggior parte degli sviluppatori sfrutta i metodi e le proprietà disponibili derivando da ViewComponent.

Quando si valuta se i componenti di visualizzazione soddisfano le specifiche di un'app, prendere in considerazione l'uso dei Razor componenti. Razor i componenti combinano anche il markup con il codice C# per produrre unità di interfaccia utente riutilizzabili. Razor i componenti sono progettati per la produttività degli sviluppatori quando forniscono logica e composizione dell'interfaccia utente sul lato client. Per altre informazioni, vedere ASP.NET Componenti di baseRazor. Per informazioni su come incorporare Razor i componenti in un'app MVC o Razor Pages, vedere Integrare componenti ASP.NET Core Razor in app ASP.NET Core.

Creare un componente di visualizzazione

Questa sezione contiene i requisiti principali per creare un componente di visualizzazione. Più avanti in questo articolo, vengono esaminati nel dettaglio tutti i passaggi e viene creato un componente di visualizzazione.

Classe del componente di visualizzazione

È possibile creare una classe del componente di visualizzazione in uno dei modi seguenti:

  • Derivazione da ViewComponent
  • Assegnando a una classe con l'attributo [ViewComponent] o derivando da una classe con l'attributo [ViewComponent]
  • Creazione di una classe in cui il nome termina con il suffisso ViewComponent

Come i controller, i componenti di visualizzazione devono essere classi pubbliche, non devono essere classi annidate e astratte. Il nome del componente di visualizzazione è il nome della classe con il ViewComponent suffisso rimosso. È anche possibile specificarlo in modo esplicito usando la proprietà Name.

Una classe del componente di visualizzazione:

  • Supporta l'inserimento delle dipendenze del costruttore
  • Non partecipa al ciclo di vita del controller, pertanto i filtri non possono essere usati in un componente di visualizzazione

Per impedire che una classe con suffisso senza distinzione tra maiuscole e minuscole ViewComponent venga considerata come componente di visualizzazione, decorare la classe con l'attributo [NonViewComponent] :

using Microsoft.AspNetCore.Mvc;

[NonViewComponent]
public class ReviewComponent
{
    public string Status(string name) => JobStatus.GetCurrentStatus(name);
}

Metodi del componente di visualizzazione

Un componente di visualizzazione definisce la logica in un:

  • InvokeAsync metodo che restituisce Task<IViewComponentResult>.
  • Invoke metodo sincrono che restituisce un oggetto IViewComponentResult.

I parametri vengono rilevati direttamente dalla chiamata del componente di visualizzazione e non dall'associazione di modelli. Un componente di visualizzazione non gestisce mai direttamente una richiesta. In genere, inizializza un modello e lo passa a una visualizzazione chiamando il metodo View. Riepilogando, i metodi del componente di visualizzazione:

  • Definiscono un metodo InvokeAsync che restituisce Task<IViewComponentResult> o un metodo sincrono Invoke che restituisce IViewComponentResult.
  • Inizializza in genere un modello e lo passa a una visualizzazione chiamando il metodo ViewComponent.View .
  • I parametri vengono rilevati dal metodo di chiamata, non da HTTP, e non vi è alcuna associazione di modelli.
  • Non è raggiungibile direttamente come endpoint HTTP. In genere vengono richiamati in una visualizzazione. Un componente di visualizzazione non gestisce mai una richiesta.
  • Sono sottoposti a overload sulla firma e non sui dettagli dalla richiesta HHTP corrente.

Percorso di ricerca della visualizzazione

Il runtime esegue la ricerca della visualizzazione nei percorsi seguenti:

  • /Views/{Nome controller}/Components/{Nome componente visualizzazione}/{Nome visualizzazione}
  • /Views/Shared/Components/{Nome componente visualizzazione}/{Nome visualizzazione}
  • /Pages/Shared/Components/{Nome componente visualizzazione}/{Nome visualizzazione}
  • /Areas/{Nome area}/Views/Shared/Components/{View Component Name}/{View Name}

Il percorso di ricerca si applica ai progetti che usano controller e visualizzazioni e Razor pagine.

Il nome di visualizzazione predefinito per un componente di visualizzazione è Default, il che significa che i file di visualizzazione verranno in genere denominati Default.cshtml. È possibile specificare un nome di visualizzazione diverso quando si crea il risultato del componente di visualizzazione o quando si chiama il View metodo .

È consigliabile denominare il file Default.cshtml di visualizzazione e usare il percorso Views/Shared/Components/{View Component Name}/{View Name} . Il PriorityList componente di visualizzazione usato in questo esempio usa Views/Shared/Components/PriorityList/Default.cshtml per la visualizzazione del componente di visualizzazione.

Personalizzare il percorso di ricerca della visualizzazione

Per personalizzare il percorso di ricerca della visualizzazione, modificare Razorla raccolta di ViewLocationFormats . Ad esempio, per cercare visualizzazioni all'interno del percorso /Components/{View Component Name}/{View Name}, aggiungere un nuovo elemento alla raccolta:

using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    });

builder.Services.AddDbContext<ToDoContext>(options =>
        options.UseInMemoryDatabase("db"));

var app = builder.Build();

// Remaining code removed for brevity.

Nel codice precedente il segnaposto {0} rappresenta il percorso Components/{View Component Name}/{View Name}.

Richiamare un componente di visualizzazione

Per usare il componente di visualizzazione, chiamare il codice seguente all'interno di una visualizzazione:

@await Component.InvokeAsync("Name of view component",
                             {Anonymous Type Containing Parameters})

I parametri vengono passati al InvokeAsync metodo . Il PriorityList componente di visualizzazione sviluppato nell'articolo viene richiamato dal file di Views/ToDo/Index.cshtml visualizzazione. Nel codice seguente il InvokeAsync metodo viene chiamato con due parametri:

</table>

<div>
    Maximum Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync("PriorityList",
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Richiamare un componente di visualizzazione come helper tag

Un componente di visualizzazione può essere richiamato come helper tag:

<div>
       Maxium Priority: @ViewData["maxPriority"] <br />
       Is Complete:  @ViewData["isDone"]
    @{
        int maxPriority = Convert.ToInt32(ViewData["maxPriority"]);
        bool isDone = Convert.ToBoolean(ViewData["isDone"]);
    }
    <vc:priority-list max-priority=maxPriority is-done=isDone>
    </vc:priority-list>
</div>

La classe scritta usando la convenzione Pascal e i parametri del metodo per gli helper tag vengono convertiti nel formato corrispondente kebab case. Per richiamare un componente di visualizzazione, l'helper tag usa l'elemento <vc></vc>. Il componente di visualizzazione viene specificato nel modo seguente:

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

Per usare un componente di visualizzazione come helper tag, registrare l'assembly contenente il componente di visualizzazione usando la direttiva @addTagHelper. Se il componente di visualizzazione si trova in un assembly denominato MyWebApp, aggiungere la direttiva seguente al _ViewImports.cshtml file :

@addTagHelper *, MyWebApp

Un componente di visualizzazione può essere registrato come helper tag a qualsiasi file che fa riferimento al componente di visualizzazione. Vedere Gestione dell'ambito dell'helper tag per altre informazioni su come registrare gli helper tag.

Il metodo InvokeAsync usato in questa esercitazione:

@await Component.InvokeAsync("PriorityList",
                 new { 
                     maxPriority =  ViewData["maxPriority"],
                     isDone = ViewData["isDone"]  }
                 )

Nel markup precedente, il componente di PriorityList visualizzazione diventa priority-list. I parametri per il componente di visualizzazione vengono passati come attributi nel formato kebab case.

Richiamare un componente di visualizzazione direttamente da un controller

I componenti di visualizzazione vengono in genere richiamati da una visualizzazione, ma possono essere richiamati direttamente da un metodo controller. Anche se i componenti di visualizzazione non definiscono endpoint come controller, è possibile implementare un'azione controller che restituisce il contenuto di un oggetto ViewComponentResult .

Nell'esempio seguente il componente di visualizzazione viene chiamato direttamente dal controller:

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

Creare un componente di visualizzazione di base

Scaricare, compilare e testare il codice di avvio. Si tratta di un progetto di base con un ToDo controller che visualizza un elenco di elementi ToDo .

List of ToDos

Aggiornare il controller per passare lo stato di priorità e completamento

Aggiornare il Index metodo per usare i parametri di stato di priorità e completamento:

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.Controllers;
public class ToDoController : Controller
{
    private readonly ToDoContext _ToDoContext;

    public ToDoController(ToDoContext context)
    {
        _ToDoContext = context;
        _ToDoContext.Database.EnsureCreated();
    }

    public IActionResult Index(int maxPriority = 2, bool isDone = false)
    {
        var model = _ToDoContext!.ToDo!.ToList();
        ViewData["maxPriority"] = maxPriority;
        ViewData["isDone"] = isDone;
        return View(model);
    }

Aggiungere una classe ViewComponent

Aggiungere una classe ViewComponent a ViewComponents/PriorityListViewComponent.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityListViewComponent : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityListViewComponent(ToDoContext context) => db = context;

    public async Task<IViewComponentResult> InvokeAsync(
                                            int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Note riguardanti il codice:

  • Le classi del componente di visualizzazione possono essere contenute in qualsiasi cartella del progetto.

  • Poiché il nome della classe PriorityListViewComponent termina con il suffisso ViewComponent, il runtime usa la stringa PriorityList quando si fa riferimento al componente della classe da una visualizzazione.

  • L'attributo [ViewComponent] può modificare il nome usato per fare riferimento a un componente di visualizzazione. Ad esempio, la classe potrebbe essere stata denominata XYZ con l'attributo seguente [ViewComponent] :

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • L'attributo [ViewComponent] nel codice precedente indica al selettore del componente di visualizzazione di usare:

    • Nome PriorityList durante la ricerca delle visualizzazioni associate al componente
    • Stringa "PriorityList" quando si fa riferimento al componente della classe da una vista.
  • Il componente usa l'inserimento delle dipendenze per rendere disponibile il contesto dei dati.

  • InvokeAsync espone un metodo che può essere chiamato da una vista e può accettare un numero arbitrario di argomenti.

  • Il metodo InvokeAsync restituisce il set di elementi ToDo che soddisfano i parametri isDone e maxPriority.

Creare la visualizzazione del componente Razor di visualizzazione

  • Creare la cartella Views/Shared/Components. Il nome di questa cartella deve essere Components.

  • Creare la cartella Views/Shared/Components/PriorityList. Questo nome della cartella deve corrispondere al nome della classe del componente di visualizzazione o al nome della classe meno il suffisso. Se viene usato l'attributo ViewComponent , il nome della classe deve corrispondere alla designazione dell'attributo.

  • Creare una Views/Shared/Components/PriorityList/Default.cshtmlRazor visualizzazione:

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    La Razor visualizzazione accetta un elenco di TodoItem e le visualizza. Se il metodo del componente InvokeAsync di visualizzazione non passa il nome della visualizzazione, default viene usato per il nome della visualizzazione per convenzione. Per sostituire lo stile predefinito per un controller specifico, aggiungere una visualizzazione alla cartella di visualizzazione specifica del controller, ad esempio Views/ToDo/Components/PriorityList/Default.cshtml.

    Se il componente di visualizzazione è specifico del controller, può essere aggiunto alla cartella specifica del controller. Ad esempio, Views/ToDo/Components/PriorityList/Default.cshtml è specifico del controller.

  • Aggiungere un oggetto div contenente una chiamata al componente dell'elenco di priorità nella parte inferiore del Views/ToDo/index.cshtml file:

    </table>
    
    <div>
        Maximum Priority: @ViewData["maxPriority"] <br />
        Is Complete:  @ViewData["isDone"]
        @await Component.InvokeAsync("PriorityList",
                         new { 
                             maxPriority =  ViewData["maxPriority"],
                             isDone = ViewData["isDone"]  }
                         )
    </div>
    

Il markup @await Component.InvokeAsync illustra la sintassi per chiamare i componenti di visualizzazione. Il primo argomento corrisponde al nome del componente che si vuole richiamare o chiamare. I parametri successivi vengono passati al componente. InvokeAsync può accettare un numero arbitrario di argomenti.

Test dell'app. La figura seguente illustra l'elenco ToDo e gli elementi con priorità:

todo list and priority items

Il componente di visualizzazione può essere chiamato direttamente dal controller:

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

priority items from IndexVC action

Specificare il nome di un componente di visualizzazione

Per un componente di visualizzazione, in alcune condizioni è possibile dover specificare una visualizzazione non predefinita. Il codice seguente illustra come specificare la visualizzazione "PVC" dal metodo InvokeAsync. Aggiornare il metodo InvokeAsync nella classe PriorityListViewComponent.

public async Task<IViewComponentResult> InvokeAsync(
                                           int maxPriority, bool isDone)
{
    string MyView = "Default";
    // If asking for all completed tasks, render with the "PVC" view.
    if (maxPriority > 3 && isDone == true)
    {
        MyView = "PVC";
    }
    var items = await GetItemsAsync(maxPriority, isDone);
    return View(MyView, items);
}

Copiare il Views/Shared/Components/PriorityList/Default.cshtml file in una vista denominata Views/Shared/Components/PriorityList/PVC.cshtml. Aggiungere un'intestazione per indicare che viene usata una visualizzazione PVC.

@model IEnumerable<ViewComponentSample.Models.TodoItem>

<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
    @foreach (var todo in Model)
    {
        <li>@todo.Name</li>
    }
</ul>

Eseguire l'app e verificare la visualizzazione PVC.

Priority View Component

Se non viene eseguito il rendering della visualizzazione PVC, verificare che venga chiamato il componente di visualizzazione con priorità 4 o superiore.

Esaminare il percorso di visualizzazione

  • Modificare il parametro relativo alla priorità impostandolo su tre o priorità inferiore perché la visualizzazione con priorità non venga restituita.

  • Rinominare temporaneamente l'oggetto Views/ToDo/Components/PriorityList/Default.cshtml in 1Default.cshtml.

  • Testare l'app, si verifica l'errore seguente:

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    
  • Copiare Views/ToDo/Components/PriorityList/1Default.cshtml in Views/Shared/Components/PriorityList/Default.cshtml.

  • Aggiungere markup alla visualizzazione del componente di visualizzazione ToDo in Shared per indicare che la visualizzazione proviene dalla cartella Shared.

  • Testare la visualizzazione del componente Shared.

ToDo output with Shared component view

Evitare stringhe hardcoded

Per la sicurezza in fase di compilazione, sostituire il nome del componente di visualizzazione hardcoded con il nome della classe. Aggiornare il file PriorityListViewComponent.cs per non usare il suffisso "ViewComponent":

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityList : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityList(ToDoContext context)
    {
        db = context;
    }

    public async Task<IViewComponentResult> InvokeAsync(
                                               int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Il file di visualizzazione :

</table>

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Un overload di Component.InvokeAsync metodo che accetta un tipo CLR usa l'operatore typeof :

</table>

<div>
    Testing typeof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(typeof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Eseguire operazioni sincrone

Il framework gestisce la chiamata di un metodo sincrono Invoke se il lavoro asincrono non è necessario. Il metodo seguente crea un componente di visualizzazione Invoke sincrono:

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListSync : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityListSync(ToDoContext context)
        {
            db = context;
        }

        public IViewComponentResult Invoke(int maxPriority, bool isDone)
        {
 
            var x = db!.ToDo!.Where(x => x.IsDone == isDone &&
                                  x.Priority <= maxPriority).ToList();
            return View(x);
        }
    }
}

Il file del componente di Razor visualizzazione:

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityListSync),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Il componente di visualizzazione viene richiamato in un Razor file ,ad esempio , Views/Home/Index.cshtmlusando uno degli approcci seguenti:

Per usare l'approccio IViewComponentHelper, chiamare Component.InvokeAsync:

@await Component.InvokeAsync(nameof(PriorityList),
                             new { maxPriority = 4, isDone = true })

Per usare l'helper tag, registrare l'assembly contenente il componente di visualizzazione usando la direttiva @addTagHelper (il componente di visualizzazione è in un assembly denominato MyWebApp):

@addTagHelper *, MyWebApp

Usare l'helper tag del componente di visualizzazione nel Razor file di markup:

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

La firma del metodo di PriorityList.Invoke è sincrona, ma Razor trova e chiama il metodo con Component.InvokeAsync nel file di markup.

Risorse aggiuntive

Componenti di visualizzazione

I componenti di visualizzazione hanno aspetti comuni con le visualizzazioni parziali, ma sono molto più efficienti. I componenti di visualizzazione non usano l'associazione di modelli, dipendono dai dati passati quando si chiama il componente di visualizzazione. Questo articolo è stato scritto usando controller e visualizzazioni, ma i componenti di visualizzazione funzionano con Razor Pages.

Un componente di visualizzazione:

  • Esegue il rendering di un blocco anziché di un'intera risposta.
  • Include la stessa separazione dei concetti e gli stessi vantaggi per i test individuati nel controller e nella visualizzazione.
  • Può contenere parametri e logica di business.
  • In genere viene richiamato da una pagina di layout.

I componenti di visualizzazione sono destinati ovunque la logica di rendering riutilizzabile troppo complessa per una visualizzazione parziale, ad esempio:

  • Menu di spostamento dinamici
  • Contrassegna il cloud, in cui esegue query sul database
  • Pannello di accesso
  • Carrello acquisti
  • Articoli recentemente pubblicati
  • Contenuto della barra laterale in un blog
  • Pannello di accesso di cui viene eseguito il rendering in ogni pagina e vengono visualizzati i collegamenti per disconnettersi o accedere, a seconda dello stato di accesso dell'utente

Un componente di visualizzazione è costituito da due parti:

  • Classe, in genere derivata da ViewComponent
  • Risultato restituito, in genere una visualizzazione.

Come i controller, un componente di visualizzazione può essere poco, ma la maggior parte degli sviluppatori sfrutta i metodi e le proprietà disponibili derivando da ViewComponent.

Quando si valuta se i componenti di visualizzazione soddisfano le specifiche di un'app, prendere in considerazione l'uso dei Razor componenti. Razor i componenti combinano anche il markup con il codice C# per produrre unità di interfaccia utente riutilizzabili. Razor i componenti sono progettati per la produttività degli sviluppatori quando forniscono logica e composizione dell'interfaccia utente sul lato client. Per altre informazioni, vedere ASP.NET Componenti di baseRazor. Per informazioni su come incorporare Razor componenti in un'app MVC o Razor Pages, vedere Prerender and integrate ASP.NET Core components (Prerender and integrate ASP.NET Core Razor components).

Creare un componente di visualizzazione

Questa sezione contiene i requisiti principali per creare un componente di visualizzazione. Più avanti in questo articolo, vengono esaminati nel dettaglio tutti i passaggi e viene creato un componente di visualizzazione.

Classe del componente di visualizzazione

È possibile creare una classe del componente di visualizzazione in uno dei modi seguenti:

  • Derivazione da ViewComponent
  • Assegnando a una classe con l'attributo [ViewComponent] o derivando da una classe con l'attributo [ViewComponent]
  • Creazione di una classe in cui il nome termina con il suffisso ViewComponent

Come i controller, i componenti di visualizzazione devono essere classi pubbliche, non devono essere classi annidate e astratte. Il nome del componente di visualizzazione è il nome della classe con il ViewComponent suffisso rimosso. È anche possibile specificarlo in modo esplicito usando la proprietà Name.

Una classe del componente di visualizzazione:

  • Supporta l'inserimento delle dipendenze del costruttore
  • Non partecipa al ciclo di vita del controller, pertanto i filtri non possono essere usati in un componente di visualizzazione

Per impedire che una classe con suffisso senza distinzione tra maiuscole e minuscole ViewComponent venga considerata come componente di visualizzazione, decorare la classe con l'attributo [NonViewComponent] :

using Microsoft.AspNetCore.Mvc;

[NonViewComponent]
public class ReviewComponent
{
    public string Status(string name) => JobStatus.GetCurrentStatus(name);
}

Metodi del componente di visualizzazione

Un componente di visualizzazione definisce la logica in un:

  • InvokeAsync metodo che restituisce Task<IViewComponentResult>.
  • Invoke metodo sincrono che restituisce un oggetto IViewComponentResult.

I parametri vengono rilevati direttamente dalla chiamata del componente di visualizzazione e non dall'associazione di modelli. Un componente di visualizzazione non gestisce mai direttamente una richiesta. In genere, inizializza un modello e lo passa a una visualizzazione chiamando il metodo View. Riepilogando, i metodi del componente di visualizzazione:

  • Definiscono un metodo InvokeAsync che restituisce Task<IViewComponentResult> o un metodo sincrono Invoke che restituisce IViewComponentResult.
  • Inizializza in genere un modello e lo passa a una visualizzazione chiamando il metodo ViewComponent.View .
  • I parametri vengono rilevati dal metodo di chiamata, non da HTTP, e non vi è alcuna associazione di modelli.
  • Non è raggiungibile direttamente come endpoint HTTP. In genere vengono richiamati in una visualizzazione. Un componente di visualizzazione non gestisce mai una richiesta.
  • Sono sottoposti a overload sulla firma e non sui dettagli dalla richiesta HHTP corrente.

Percorso di ricerca della visualizzazione

Il runtime esegue la ricerca della visualizzazione nei percorsi seguenti:

  • /Views/{Nome controller}/Components/{Nome componente visualizzazione}/{Nome visualizzazione}
  • /Views/Shared/Components/{Nome componente visualizzazione}/{Nome visualizzazione}
  • /Pages/Shared/Components/{Nome componente visualizzazione}/{Nome visualizzazione}
  • /Areas/{Nome area}/Views/Shared/Components/{View Component Name}/{View Name}

Il percorso di ricerca si applica ai progetti che usano controller e visualizzazioni e Razor pagine.

Il nome di visualizzazione predefinito per un componente di visualizzazione è Default, il che significa che i file di visualizzazione verranno in genere denominati Default.cshtml. È possibile specificare un nome di visualizzazione diverso quando si crea il risultato del componente di visualizzazione o quando si chiama il View metodo .

È consigliabile denominare il file Default.cshtml di visualizzazione e usare il percorso Views/Shared/Components/{View Component Name}/{View Name} . Il PriorityList componente di visualizzazione usato in questo esempio usa Views/Shared/Components/PriorityList/Default.cshtml per la visualizzazione del componente di visualizzazione.

Personalizzare il percorso di ricerca della visualizzazione

Per personalizzare il percorso di ricerca della visualizzazione, modificare Razorla raccolta di ViewLocationFormats . Ad esempio, per cercare visualizzazioni all'interno del percorso /Components/{View Component Name}/{View Name}, aggiungere un nuovo elemento alla raccolta:

using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    });

builder.Services.AddDbContext<ToDoContext>(options =>
        options.UseInMemoryDatabase("db"));

var app = builder.Build();

// Remaining code removed for brevity.

Nel codice precedente il segnaposto {0} rappresenta il percorso Components/{View Component Name}/{View Name}.

Richiamare un componente di visualizzazione

Per usare il componente di visualizzazione, chiamare il codice seguente all'interno di una visualizzazione:

@await Component.InvokeAsync("Name of view component",
                             {Anonymous Type Containing Parameters})

I parametri vengono passati al InvokeAsync metodo . Il PriorityList componente di visualizzazione sviluppato nell'articolo viene richiamato dal file di Views/ToDo/Index.cshtml visualizzazione. Nel codice seguente il InvokeAsync metodo viene chiamato con due parametri:

</table>

<div>
    Maximum Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync("PriorityList",
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Richiamare un componente di visualizzazione come helper tag

Un componente di visualizzazione può essere richiamato come helper tag:

<div>
       Maxium Priority: @ViewData["maxPriority"] <br />
       Is Complete:  @ViewData["isDone"]
    @{
        int maxPriority = Convert.ToInt32(ViewData["maxPriority"]);
        bool isDone = Convert.ToBoolean(ViewData["isDone"]);
    }
    <vc:priority-list max-priority=maxPriority is-done=isDone>
    </vc:priority-list>
</div>

La classe scritta usando la convenzione Pascal e i parametri del metodo per gli helper tag vengono convertiti nel formato corrispondente kebab case. Per richiamare un componente di visualizzazione, l'helper tag usa l'elemento <vc></vc>. Il componente di visualizzazione viene specificato nel modo seguente:

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

Per usare un componente di visualizzazione come helper tag, registrare l'assembly contenente il componente di visualizzazione usando la direttiva @addTagHelper. Se il componente di visualizzazione si trova in un assembly denominato MyWebApp, aggiungere la direttiva seguente al _ViewImports.cshtml file :

@addTagHelper *, MyWebApp

Un componente di visualizzazione può essere registrato come helper tag a qualsiasi file che fa riferimento al componente di visualizzazione. Vedere Gestione dell'ambito dell'helper tag per altre informazioni su come registrare gli helper tag.

Il metodo InvokeAsync usato in questa esercitazione:

@await Component.InvokeAsync("PriorityList",
                 new { 
                     maxPriority =  ViewData["maxPriority"],
                     isDone = ViewData["isDone"]  }
                 )

Nel markup precedente, il componente di PriorityList visualizzazione diventa priority-list. I parametri per il componente di visualizzazione vengono passati come attributi nel formato kebab case.

Richiamare un componente di visualizzazione direttamente da un controller

I componenti di visualizzazione vengono in genere richiamati da una visualizzazione, ma possono essere richiamati direttamente da un metodo controller. Anche se i componenti di visualizzazione non definiscono endpoint come controller, è possibile implementare un'azione controller che restituisce il contenuto di un oggetto ViewComponentResult .

Nell'esempio seguente il componente di visualizzazione viene chiamato direttamente dal controller:

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

Creare un componente di visualizzazione di base

Scaricare, compilare e testare il codice di avvio. Si tratta di un progetto di base con un ToDo controller che visualizza un elenco di elementi ToDo .

List of ToDos

Aggiornare il controller per passare lo stato di priorità e completamento

Aggiornare il Index metodo per usare i parametri di stato di priorità e completamento:

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.Controllers;
public class ToDoController : Controller
{
    private readonly ToDoContext _ToDoContext;

    public ToDoController(ToDoContext context)
    {
        _ToDoContext = context;
        _ToDoContext.Database.EnsureCreated();
    }

    public IActionResult Index(int maxPriority = 2, bool isDone = false)
    {
        var model = _ToDoContext!.ToDo!.ToList();
        ViewData["maxPriority"] = maxPriority;
        ViewData["isDone"] = isDone;
        return View(model);
    }

Aggiungere una classe ViewComponent

Aggiungere una classe ViewComponent a ViewComponents/PriorityListViewComponent.cs:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityListViewComponent : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityListViewComponent(ToDoContext context) => db = context;

    public async Task<IViewComponentResult> InvokeAsync(
                                            int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Note riguardanti il codice:

  • Le classi del componente di visualizzazione possono essere contenute in qualsiasi cartella del progetto.

  • Poiché il nome della classe PriorityListViewComponent termina con il suffisso ViewComponent, il runtime usa la stringa PriorityList quando si fa riferimento al componente della classe da una visualizzazione.

  • L'attributo [ViewComponent] può modificare il nome usato per fare riferimento a un componente di visualizzazione. Ad esempio, la classe potrebbe essere stata denominata XYZ con l'attributo seguente [ViewComponent] :

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • L'attributo [ViewComponent] nel codice precedente indica al selettore del componente di visualizzazione di usare:

    • Nome PriorityList durante la ricerca delle visualizzazioni associate al componente
    • Stringa "PriorityList" quando si fa riferimento al componente della classe da una vista.
  • Il componente usa l'inserimento delle dipendenze per rendere disponibile il contesto dei dati.

  • InvokeAsync espone un metodo che può essere chiamato da una vista e può accettare un numero arbitrario di argomenti.

  • Il metodo InvokeAsync restituisce il set di elementi ToDo che soddisfano i parametri isDone e maxPriority.

Creare la visualizzazione del componente Razor di visualizzazione

  • Creare la cartella Views/Shared/Components. Il nome di questa cartella deve essere Components.

  • Creare la cartella Views/Shared/Components/PriorityList. Questo nome della cartella deve corrispondere al nome della classe del componente di visualizzazione o al nome della classe meno il suffisso. Se viene usato l'attributo ViewComponent , il nome della classe deve corrispondere alla designazione dell'attributo.

  • Creare una Views/Shared/Components/PriorityList/Default.cshtmlRazor visualizzazione:

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    La Razor visualizzazione accetta un elenco di TodoItem e le visualizza. Se il metodo del componente InvokeAsync di visualizzazione non passa il nome della visualizzazione, default viene usato per il nome della visualizzazione per convenzione. Per sostituire lo stile predefinito per un controller specifico, aggiungere una visualizzazione alla cartella di visualizzazione specifica del controller, ad esempio Views/ToDo/Components/PriorityList/Default.cshtml.

    Se il componente di visualizzazione è specifico del controller, può essere aggiunto alla cartella specifica del controller. Ad esempio, Views/ToDo/Components/PriorityList/Default.cshtml è specifico del controller.

  • Aggiungere un oggetto div contenente una chiamata al componente dell'elenco di priorità nella parte inferiore del Views/ToDo/index.cshtml file:

    </table>
    
    <div>
        Maximum Priority: @ViewData["maxPriority"] <br />
        Is Complete:  @ViewData["isDone"]
        @await Component.InvokeAsync("PriorityList",
                         new { 
                             maxPriority =  ViewData["maxPriority"],
                             isDone = ViewData["isDone"]  }
                         )
    </div>
    

Il markup @await Component.InvokeAsync illustra la sintassi per chiamare i componenti di visualizzazione. Il primo argomento corrisponde al nome del componente che si vuole richiamare o chiamare. I parametri successivi vengono passati al componente. InvokeAsync può accettare un numero arbitrario di argomenti.

Test dell'app. La figura seguente illustra l'elenco ToDo e gli elementi con priorità:

todo list and priority items

Il componente di visualizzazione può essere chiamato direttamente dal controller:

public IActionResult IndexVC(int maxPriority = 2, bool isDone = false)
{
    return ViewComponent("PriorityList",
        new { 
           maxPriority = maxPriority,
           isDone = isDone
        });
}

priority items from IndexVC action

Specificare il nome di un componente di visualizzazione

Per un componente di visualizzazione, in alcune condizioni è possibile dover specificare una visualizzazione non predefinita. Il codice seguente illustra come specificare la visualizzazione "PVC" dal metodo InvokeAsync. Aggiornare il metodo InvokeAsync nella classe PriorityListViewComponent.

public async Task<IViewComponentResult> InvokeAsync(
                                           int maxPriority, bool isDone)
{
    string MyView = "Default";
    // If asking for all completed tasks, render with the "PVC" view.
    if (maxPriority > 3 && isDone == true)
    {
        MyView = "PVC";
    }
    var items = await GetItemsAsync(maxPriority, isDone);
    return View(MyView, items);
}

Copiare il Views/Shared/Components/PriorityList/Default.cshtml file in una vista denominata Views/Shared/Components/PriorityList/PVC.cshtml. Aggiungere un'intestazione per indicare che viene usata una visualizzazione PVC.

@model IEnumerable<ViewComponentSample.Models.TodoItem>

<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
    @foreach (var todo in Model)
    {
        <li>@todo.Name</li>
    }
</ul>

Eseguire l'app e verificare la visualizzazione PVC.

Priority View Component

Se non viene eseguito il rendering della visualizzazione PVC, verificare che venga chiamato il componente di visualizzazione con priorità 4 o superiore.

Esaminare il percorso di visualizzazione

  • Modificare il parametro relativo alla priorità impostandolo su tre o priorità inferiore perché la visualizzazione con priorità non venga restituita.

  • Rinominare temporaneamente l'oggetto Views/ToDo/Components/PriorityList/Default.cshtml in 1Default.cshtml.

  • Testare l'app, si verifica l'errore seguente:

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    
  • Copiare Views/ToDo/Components/PriorityList/1Default.cshtml in Views/Shared/Components/PriorityList/Default.cshtml.

  • Aggiungere markup alla visualizzazione del componente di visualizzazione ToDo in Shared per indicare che la visualizzazione proviene dalla cartella Shared.

  • Testare la visualizzazione del componente Shared.

ToDo output with Shared component view

Evitare stringhe hardcoded

Per la sicurezza in fase di compilazione, sostituire il nome del componente di visualizzazione hardcoded con il nome della classe. Aggiornare il file PriorityListViewComponent.cs per non usare il suffisso "ViewComponent":

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents;

public class PriorityList : ViewComponent
{
    private readonly ToDoContext db;

    public PriorityList(ToDoContext context)
    {
        db = context;
    }

    public async Task<IViewComponentResult> InvokeAsync(
                                               int maxPriority, bool isDone)
    {
        var items = await GetItemsAsync(maxPriority, isDone);
        return View(items);
    }

    private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
    {
        return db!.ToDo!.Where(x => x.IsDone == isDone &&
                             x.Priority <= maxPriority).ToListAsync();
    }
}

Il file di visualizzazione :

</table>

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Un overload di Component.InvokeAsync metodo che accetta un tipo CLR usa l'operatore typeof :

</table>

<div>
    Testing typeof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(typeof(PriorityList),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Eseguire operazioni sincrone

Il framework gestisce la chiamata di un metodo sincrono Invoke se il lavoro asincrono non è necessario. Il metodo seguente crea un componente di visualizzazione Invoke sincrono:

using Microsoft.AspNetCore.Mvc;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListSync : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityListSync(ToDoContext context)
        {
            db = context;
        }

        public IViewComponentResult Invoke(int maxPriority, bool isDone)
        {
 
            var x = db!.ToDo!.Where(x => x.IsDone == isDone &&
                                  x.Priority <= maxPriority).ToList();
            return View(x);
        }
    }
}

Il file del componente di Razor visualizzazione:

<div>
    Testing nameof(PriorityList) <br />

    Maxium Priority: @ViewData["maxPriority"] <br />
    Is Complete:  @ViewData["isDone"]
    @await Component.InvokeAsync(nameof(PriorityListSync),
                     new { 
                         maxPriority =  ViewData["maxPriority"],
                         isDone = ViewData["isDone"]  }
                     )
</div>

Il componente di visualizzazione viene richiamato in un Razor file ,ad esempio , Views/Home/Index.cshtmlusando uno degli approcci seguenti:

Per usare l'approccio IViewComponentHelper, chiamare Component.InvokeAsync:

@await Component.InvokeAsync(nameof(PriorityList),
                             new { maxPriority = 4, isDone = true })

Per usare l'helper tag, registrare l'assembly contenente il componente di visualizzazione usando la direttiva @addTagHelper (il componente di visualizzazione è in un assembly denominato MyWebApp):

@addTagHelper *, MyWebApp

Usare l'helper tag del componente di visualizzazione nel Razor file di markup:

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

La firma del metodo di PriorityList.Invoke è sincrona, ma Razor trova e chiama il metodo con Component.InvokeAsync nel file di markup.

Risorse aggiuntive

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

Componenti di visualizzazione

I componenti di visualizzazione hanno aspetti comuni con le visualizzazioni parziali, ma sono molto più efficienti. I componenti di visualizzazione non usano l'associazione di modelli. Dipendono soltanto dai dati specificati in fase di chiamata. Questo articolo è stato scritto usando controller e visualizzazioni, ma i componenti di visualizzazione funzionano anche con Razor Pages.

Un componente di visualizzazione:

  • Esegue il rendering di un blocco anziché di un'intera risposta.
  • Include la stessa separazione dei concetti e gli stessi vantaggi per i test individuati nel controller e nella visualizzazione.
  • Può contenere parametri e logica di business.
  • In genere viene richiamato da una pagina di layout.

I componenti di visualizzazione possono essere impiegati in un punto qualsiasi della logica di rendering riutilizzabile che risulta troppo complessa per una visualizzazione parziale, ad esempio:

  • Menu di spostamento dinamici
  • Tag cloud (nel quale viene eseguita una query del database)
  • Pannello di accesso
  • Carrello acquisti
  • Articoli recentemente pubblicati
  • Contenuto dell'intestazione laterale in un blog tradizionale
  • Pannello di accesso che viene eseguito in ogni pagina e che visualizza il collegamento di accesso o di disconnessione, a seconda dello stato di accesso dell'utente

Un componente di visualizzazione è costituito da due parti: la classe (in genere derivata da ViewComponent) e il risultato restituito (in genere una visualizzazione). Come i controller, un componente di visualizzazione può essere poco, ma la maggior parte degli sviluppatori sfrutta i metodi e le proprietà disponibili derivando da ViewComponent.

Quando si valuta se i componenti di visualizzazione soddisfano le specifiche di un'app, prendere in considerazione l'uso dei Razor componenti. Razor i componenti combinano anche il markup con il codice C# per produrre unità di interfaccia utente riutilizzabili. Razor i componenti sono progettati per la produttività degli sviluppatori quando forniscono logica e composizione dell'interfaccia utente sul lato client. Per altre informazioni, vedere ASP.NET Componenti di baseRazor. Per informazioni su come incorporare Razor componenti in un'app MVC o Razor Pages, vedere Prerender and integrate ASP.NET Core components (Prerender and integrate ASP.NET Core Razor components).

Creazione di un componente di visualizzazione

Questa sezione contiene i requisiti principali per creare un componente di visualizzazione. Più avanti in questo articolo, vengono esaminati nel dettaglio tutti i passaggi e viene creato un componente di visualizzazione.

Classe del componente di visualizzazione

È possibile creare una classe del componente di visualizzazione in uno dei modi seguenti:

  • Derivando da ViewComponent
  • Assegnando a una classe con l'attributo [ViewComponent] o derivando da una classe con l'attributo [ViewComponent]
  • Creando una classe in cui il nome termina con il suffisso ViewComponent

Come i controller, i componenti di visualizzazione devono essere classi pubbliche, non devono essere classi annidate e astratte. Il nome del componente di visualizzazione corrisponde al nome della classe privato del suffisso "ViewComponent". È anche possibile specificarlo in modo esplicito usando la proprietà ViewComponentAttribute.Name.

Una classe del componente di visualizzazione:

  • Supporta pienamente l'inserimento delle dipendenze del costruttore
  • Non partecipa al ciclo di vita del controller, non è quindi possibile usare i filtri in un componente di visualizzazione

Per arrestare una classe con un suffisso ViewComponent senza distinzione tra maiuscole e minuscole, decorare la classe con l'attributo [NonViewComponent]:

[NonViewComponent]
public class ReviewComponent
{
    // ...

Metodi del componente di visualizzazione

Un componente di visualizzazione definisce la propria logica in un metodo InvokeAsync che restituisce Task<IViewComponentResult> o in un metodo asincrono Invoke che restituisce IViewComponentResult. I parametri vengono rilevati direttamente dalla chiamata del componente di visualizzazione e non dall'associazione di modelli. Un componente di visualizzazione non gestisce mai direttamente una richiesta. In genere, inizializza un modello e lo passa a una visualizzazione chiamando il metodo View. Riepilogando, i metodi del componente di visualizzazione:

  • Definiscono un metodo InvokeAsync che restituisce Task<IViewComponentResult> o un metodo sincrono Invoke che restituisce IViewComponentResult.
  • In genere, inizializzano un modello e lo passano a una visualizzazione chiamando il metodo ViewComponentView.
  • I parametri vengono rilevati dal metodo di chiamata, non da HTTP, e non vi è alcuna associazione di modelli.
  • Non sono raggiungibili direttamente come un endpoint HTTP. Vengono richiamati dal codice (in genere in una vista). Un componente di visualizzazione non gestisce mai una richiesta.
  • Sono sottoposti a overload sulla firma e non sui dettagli dalla richiesta HHTP corrente.

Percorso di ricerca della visualizzazione

Il runtime esegue la ricerca della visualizzazione nei percorsi seguenti:

  • /Views/{Nome controller}/Components/{Nome componente visualizzazione}/{Nome visualizzazione}
  • /Views/Shared/Components/{Nome componente visualizzazione}/{Nome visualizzazione}
  • /Pages/Shared/Components/{Nome componente visualizzazione}/{Nome visualizzazione}
  • /Areas/{Nome area}/Views/Shared/Components/{View Component Name}/{View Name}

Il percorso di ricerca si applica ai progetti che usano controller e visualizzazioni e Razor pagine.

Il nome di visualizzazione predefinito per un componente di visualizzazione è Predefinito, il che significa che il file di visualizzazione verrà in genere denominato Default.cshtml. È possibile specificare un nome di visualizzazione diverso quando si crea il risultato del componente di visualizzazione o quando si chiama il metodo View.

È consigliabile denominare il file Default.cshtml di visualizzazione e usare il percorso Views/Shared/Components/{View Component Name}/{View Name} . Il PriorityList componente di visualizzazione usato in questo esempio usa Views/Shared/Components/PriorityList/Default.cshtml per la visualizzazione del componente di visualizzazione.

Personalizzare il percorso di ricerca della visualizzazione

Per personalizzare il percorso di ricerca della visualizzazione, modificare Razorla raccolta di ViewLocationFormats . Ad esempio, per cercare visualizzazioni all'interno del percorso "/Components/{View Component Name}/{View Name}", aggiungere un nuovo elemento alla raccolta:

services.AddMvc()
    .AddRazorOptions(options =>
    {
        options.ViewLocationFormats.Add("/{0}.cshtml");
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Nel codice precedente il segnaposto "{0}" rappresenta il percorso "Components/{View Component Name}/{View Component Name}".

Chiamata di un componente di visualizzazione

Per usare il componente di visualizzazione, chiamare il codice seguente all'interno di una visualizzazione:

@await Component.InvokeAsync("Name of view component", {Anonymous Type Containing Parameters})

I parametri saranno passati al metodo InvokeAsync. Il PriorityList componente di visualizzazione sviluppato nell'articolo viene richiamato dal file di Views/ToDo/Index.cshtml visualizzazione. Nell'esempio seguente il metodo InvokeAsync viene chiamato con due parametri:

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

Chiamata di un componente di visualizzazione come helper tag

Per ASP.NET Core 1.1 e versioni successive, è possibile richiamare un componente di visualizzazione come helper tag:

<vc:priority-list max-priority="2" is-done="false">
</vc:priority-list>

La classe scritta usando la convenzione Pascal e i parametri del metodo per gli helper tag vengono convertiti nel formato corrispondente kebab case. Per richiamare un componente di visualizzazione, l'helper tag usa l'elemento <vc></vc>. Il componente di visualizzazione viene specificato nel modo seguente:

<vc:[view-component-name]
  parameter1="parameter1 value"
  parameter2="parameter2 value">
</vc:[view-component-name]>

Per usare un componente di visualizzazione come helper tag, registrare l'assembly contenente il componente di visualizzazione usando la direttiva @addTagHelper. Se il componente di visualizzazione si trova in un assembly denominato MyWebApp, aggiungere la direttiva seguente al _ViewImports.cshtml file :

@addTagHelper *, MyWebApp

È possibile registrare un componente di visualizzazione come helper tag per qualsiasi file che fa riferimento al componente di visualizzazione. Vedere Gestione dell'ambito dell'helper tag per altre informazioni su come registrare gli helper tag.

Il metodo InvokeAsync usato in questa esercitazione:

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

Nel markup dell'helper tag:

<vc:priority-list max-priority="2" is-done="false">
</vc:priority-list>

Nell'esempio precedente il componente di visualizzazione PriorityList diventa priority-list. I parametri per il componente di visualizzazione vengono passati come attributi nel formato kebab case.

Richiamo di un componente di visualizzazione direttamente da un controller

I componenti di visualizzazione sono solitamente richiamati da una visualizzazione, ma possono essere richiamati direttamente da un metodo del controller. A differenza dei controller i componenti di visualizzazione non definiscono endpoint. È tuttavia possibile implementare semplicemente un'azione del controller in modo che venga restituito il contenuto di un oggetto ViewComponentResult.

In questo esempio il componente di visualizzazione viene chiamato direttamente dal controller:

public IActionResult IndexVC()
{
    return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

Procedura dettagliata: creazione di un componente di visualizzazione semplice

Scaricare, compilare e testare il codice di avvio. Si tratta di un progetto semplice con un controller ToDo che visualizza un elenco di elementi ToDo.

List of ToDos

Aggiungere una classe ViewComponent

Creare una cartella ViewComponents e aggiungere la classe PriorityListViewComponent seguente:

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityListViewComponent : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityListViewComponent(ToDoContext context)
        {
            db = context;
        }

        public async Task<IViewComponentResult> InvokeAsync(
        int maxPriority, bool isDone)
        {
            var items = await GetItemsAsync(maxPriority, isDone);
            return View(items);
        }
        private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return db.ToDo.Where(x => x.IsDone == isDone &&
                                 x.Priority <= maxPriority).ToListAsync();
        }
    }
}

Note riguardanti il codice:

  • Le classi del componente di visualizzazione possono essere contenute in qualsiasi cartella del progetto.

  • Poiché il nome della classe PriorityListViewComponent termina con il suffisso ViewComponent, il runtime usa la stringa PriorityList quando si fa riferimento al componente della classe da una visualizzazione.

  • L'attributo [ViewComponent] può modificare il nome usato per fare riferimento a un componente di visualizzazione. Ad esempio, la classe potrebbe essere stata denominata XYZ con l'attributo ViewComponent :

    [ViewComponent(Name = "PriorityList")]
       public class XYZ : ViewComponent
    
  • L'attributo [ViewComponent] nel codice precedente indica al selettore del componente di visualizzazione di usare:

    • Nome PriorityList durante la ricerca delle visualizzazioni associate al componente
    • Stringa "PriorityList" quando si fa riferimento al componente della classe da una vista.
  • Il componente usa l'inserimento delle dipendenze per rendere disponibile il contesto dei dati.

  • InvokeAsync espone un metodo che può essere chiamato da una visualizzazione e può accettare un numero arbitrario di argomenti.

  • Il metodo InvokeAsync restituisce il set di elementi ToDo che soddisfano i parametri isDone e maxPriority.

Creare la visualizzazione del componente Razor di visualizzazione

  • Creare la cartella Views/Shared/Components. Questa cartella deve essere denominata Components.

  • Creare la cartella Views/Shared/Components/PriorityList. Il nome di questa cartella deve corrispondere al nome della classe del componente di visualizzazione oppure al nome della classe privato del suffisso (se è stata adottata la convenzione ed è stato usato il suffisso ViewComponent nel nome della classe). Se è stato usato l'attributo ViewComponent, il nome della classe dovrà corrispondere alla designazione dell'attributo.

  • Creare una Views/Shared/Components/PriorityList/Default.cshtmlRazor visualizzazione:

    @model IEnumerable<ViewComponentSample.Models.TodoItem>
    
    <h3>Priority Items</h3>
    <ul>
        @foreach (var todo in Model)
        {
            <li>@todo.Name</li>
        }
    </ul>
    

    La Razor visualizzazione accetta un elenco di TodoItem e le visualizza. Se il metodo InvokeAsync del componente di visualizzazione non passa il nome della visualizzazione (come in questo esempio), per convenzione viene usato Default come nome della visualizzazione. Più avanti nell'esercitazione viene illustrato come passare il nome della visualizzazione. Per sostituire lo stile predefinito per un controller specifico, aggiungere una visualizzazione alla cartella di visualizzazione specifica del controller, ad esempio Views/ToDo/Components/PriorityList/Default.cshtml.

    Se il componente di visualizzazione è specifico del controller, è possibile aggiungerlo alla cartella specifica del controller (Views/ToDo/Components/PriorityList/Default.cshtml).

  • Aggiungere un oggetto div contenente una chiamata al componente dell'elenco di priorità nella parte inferiore del Views/ToDo/index.cshtml file:

    </table>
    <div>
        @await Component.InvokeAsync("PriorityList", new { maxPriority = 2, isDone = false })
    </div>
    

Il markup @await Component.InvokeAsync illustra la sintassi per chiamare i componenti di visualizzazione. Il primo argomento corrisponde al nome del componente che si vuole richiamare o chiamare. I parametri successivi vengono passati al componente. InvokeAsync può accettare un numero arbitrario di argomenti.

Test dell'app. La figura seguente illustra l'elenco ToDo e gli elementi con priorità:

todo list and priority items

È anche possibile chiamare il componente di visualizzazione direttamente dal controller:

public IActionResult IndexVC()
{
    return ViewComponent("PriorityList", new { maxPriority = 3, isDone = false });
}

priority items from IndexVC action

Impostazione di un nome di visualizzazione

Per un componente di visualizzazione, in alcune condizioni è possibile dover specificare una visualizzazione non predefinita. Il codice seguente illustra come specificare la visualizzazione "PVC" dal metodo InvokeAsync. Aggiornare il metodo InvokeAsync nella classe PriorityListViewComponent.

public async Task<IViewComponentResult> InvokeAsync(
    int maxPriority, bool isDone)
{
    string MyView = "Default";
    // If asking for all completed tasks, render with the "PVC" view.
    if (maxPriority > 3 && isDone == true)
    {
        MyView = "PVC";
    }
    var items = await GetItemsAsync(maxPriority, isDone);
    return View(MyView, items);
}

Copiare il Views/Shared/Components/PriorityList/Default.cshtml file in una vista denominata Views/Shared/Components/PriorityList/PVC.cshtml. Aggiungere un'intestazione per indicare che viene usata una visualizzazione PVC.

@model IEnumerable<ViewComponentSample.Models.TodoItem>

<h2> PVC Named Priority Component View</h2>
<h4>@ViewBag.PriorityMessage</h4>
<ul>
    @foreach (var todo in Model)
    {
        <li>@todo.Name</li>
    }
</ul>

Aggiornare Views/ToDo/Index.cshtml:

@await Component.InvokeAsync("PriorityList", new { maxPriority = 4, isDone = true })

Eseguire l'app e verificare la visualizzazione PVC.

Priority View Component

Se non viene eseguito il rendering della visualizzazione PVC, verificare che si stia chiamando il componente di visualizzazione con priorità pari a 4 o superiore.

Esaminare il percorso di visualizzazione

  • Modificare il parametro relativo alla priorità impostandolo su tre o priorità inferiore perché la visualizzazione con priorità non venga restituita.

  • Rinominare temporaneamente l'oggetto Views/ToDo/Components/PriorityList/Default.cshtml in 1Default.cshtml.

  • Testare l'app. Verrà visualizzato il messaggio seguente:

    An unhandled exception occurred while processing the request.
    InvalidOperationException: The view 'Components/PriorityList/Default' wasn't found. The following locations were searched:
    /Views/ToDo/Components/PriorityList/Default.cshtml
    /Views/Shared/Components/PriorityList/Default.cshtml
    EnsureSuccessful
    
  • Copiare Views/ToDo/Components/PriorityList/1Default.cshtml in Views/Shared/Components/PriorityList/Default.cshtml.

  • Aggiungere markup alla visualizzazione del componente di visualizzazione ToDo in Shared per indicare che la visualizzazione proviene dalla cartella Shared.

  • Testare la visualizzazione del componente Shared.

ToDo output with Shared component view

Evitare stringhe hardcoded

Per garantire la sicurezza in fase di compilazione, è possibile sostituire il nome del componente di compilazione hardcoded con il nome della classe. Creare il componente di visualizzazione senza il suffisso "ViewComponent":

using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ViewComponentSample.Models;

namespace ViewComponentSample.ViewComponents
{
    public class PriorityList : ViewComponent
    {
        private readonly ToDoContext db;

        public PriorityList(ToDoContext context)
        {
            db = context;
        }

        public async Task<IViewComponentResult> InvokeAsync(
        int maxPriority, bool isDone)
        {
            var items = await GetItemsAsync(maxPriority, isDone);
            return View(items);
        }
        private Task<List<TodoItem>> GetItemsAsync(int maxPriority, bool isDone)
        {
            return db.ToDo.Where(x => x.IsDone == isDone &&
                                 x.Priority <= maxPriority).ToListAsync();
        }
    }
}

Aggiungere un'istruzione using al file di Razor visualizzazione e usare l'operatore nameof :

@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem>

    <h2>ToDo nameof</h2>
    <!-- Markup removed for brevity.  -->

    <div>

        @*
            Note: 
            To use the below line, you need to #define no_suffix in ViewComponents/PriorityList.cs or it won't compile.
            By doing so it will cause a problem to index as there will be multiple viewcomponents 
            with the same name after the compiler removes the suffix "ViewComponent"
        *@

        @*@await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })*@
    </div>

È possibile usare un overload di Component.InvokeAsync metodo che accetta un tipo CLR. Ricordarsi di usare l'operatore typeof in questo caso:

@using ViewComponentSample.Models
@using ViewComponentSample.ViewComponents
@model IEnumerable<TodoItem>

<h2>ToDo typeof</h2>
<!-- Markup removed for brevity.  -->

<div>
    @await Component.InvokeAsync(typeof(PriorityListViewComponent), new { maxPriority = 4, isDone = true })
</div>

Eseguire operazioni sincrone

Il framework gestisce la chiamata di un metodo Invoke sincrono se non è necessario eseguire operazioni asincrone. Il metodo seguente crea un componente di visualizzazione Invoke sincrono:

public class PriorityList : ViewComponent
{
    public IViewComponentResult Invoke(int maxPriority, bool isDone)
    {
        var items = new List<string> { $"maxPriority: {maxPriority}", $"isDone: {isDone}" };
        return View(items);
    }
}

Il file del componente di Razor visualizzazione elenca le stringhe passate al Invoke metodo (Views/Home/Components/PriorityList/Default.cshtml):

@model List<string>

<h3>Priority Items</h3>
<ul>
    @foreach (var item in Model)
    {
        <li>@item</li>
    }
</ul>

Il componente di visualizzazione viene richiamato in un Razor file ,ad esempio , Views/Home/Index.cshtmlusando uno degli approcci seguenti:

Per usare l'approccio IViewComponentHelper, chiamare Component.InvokeAsync:

@await Component.InvokeAsync(nameof(PriorityList), new { maxPriority = 4, isDone = true })

Per usare l'helper tag, registrare l'assembly contenente il componente di visualizzazione usando la direttiva @addTagHelper (il componente di visualizzazione è in un assembly denominato MyWebApp):

@addTagHelper *, MyWebApp

Usare l'helper tag del componente di visualizzazione nel Razor file di markup:

<vc:priority-list max-priority="999" is-done="false">
</vc:priority-list>

La firma del metodo di PriorityList.Invoke è sincrona, ma Razor trova e chiama il metodo con Component.InvokeAsync nel file di markup.

Tutti i parametri del componente di visualizzazione sono obbligatori

Ogni parametro in un componente di visualizzazione è un attributo obbligatorio. Vedere il problema in GitHub. Se un parametro viene omesso:

  • La firma del metodo InvokeAsync non corrisponde, quindi il metodo non verrà eseguito.
  • ViewComponent non esegue il rendering dei markup.
  • Non vengono generati errori.

Risorse aggiuntive