Creare componenti riutilizzabili dell'interfaccia utente con Blazor

Suggerimento

Questo contenuto è un estratto dell'eBook, Blazor per gli sviluppatori di Web Forms ASP.NET per Azure, disponibile in .NET Docs o come PDF scaricabile gratuitamente che può essere letto offline.

Blazor-for-ASP-NET-Web-Forms-Developers eBook cover thumbnail.

Uno degli aspetti importanti di Web Forms ASP.NET è il modo in cui questo consente l'incapsulamento di parti riutilizzabili di codice dell'interfaccia utente (UI) in controlli riutilizzabili dell'interfaccia utente. I controlli utente personalizzati possono essere definiti nel markup usando file .ascx. Inoltre, è possibile compilare controlli server elaborati nel codice con supporto completo della finestra di progettazione.

Blazor supporta anche l'incapsulamento dell'interfaccia utente tramite i componenti. Un componente:

  • è un blocco autonomo dell'interfaccia utente.
  • Mantiene lo stato e la logica di rendering.
  • Può definire gestori eventi dell'interfaccia utente, associare ai dati di input e gestire il proprio ciclo di vita.
  • Viene in genere definito in un file .razor usando la sintassi Razor.

Presentazione di Razor

Razor è un linguaggio leggero di modelli di markup basato su HTML e C#. Con Razor è possibile eseguire facilmente la transizione tra markup e codice C# per definire la logica di rendering dei componenti. Quando il file .razor viene compilato, la logica di rendering viene acquisita in modo strutturato in una classe .NET. Il nome della classe compilata viene ricavato dal nome del file .razor. Lo spazio dei nomi viene ricavato dallo spazio dei nomi predefinito per il progetto e per il percorso della cartella oppure è possibile specificare in modo esplicito lo spazio dei nomi usando la direttiva @namespace (più avanti nelle direttive Razor).

La logica di rendering di un componente viene creata usando il normale markup HTML con la logica dinamica aggiunta tramite C#. Il carattere @ viene usato per la transizione a C#. Razor riesce solitamente a capire quando si torna al codice HTML. Ad esempio, il componente seguente esegue il rendering di un tag <p> con l'ora corrente:

<p>@DateTime.Now</p>

Per specificare in modo esplicito l'inizio e la fine di un'espressione C#, usare le parentesi:

<p>@(DateTime.Now)</p>

Razor semplifica anche l'uso del flusso di controllo C# nella logica di rendering. Ad esempio, è possibile eseguire il rendering condizionale di un codice HTML simile al seguente:

@if (value % 2 == 0)
{
    <p>The value was even.</p>
}

In alternativa, è possibile generare un elenco di elementi usando un normale ciclo C# foreach simile al seguente:

<ul>
@foreach (var item in items)
{
    <li>@item.Text</li>
}
</ul>

Le direttive Razor, come ad esempio le direttive in Web Forms ASP.NET, controllano molti aspetti della compilazione di un componente Razor. Gli esempi includono i componenti:

  • Spazio dei nomi
  • Classe base
  • Interfacce implementate
  • Parametri generici
  • Spazi dei nomi importati
  • Route

Le direttive Razor iniziano con il carattere @ e vengono in genere usate all'inizio di una nuova riga all'inizio del file. Ad esempio, la direttiva @namespace definisce lo spazio dei nomi del componente:

@namespace MyComponentNamespace

La tabella seguente riepiloga le varie direttive Razor usate in Blazor e i relativi equivalenti Web Forms ASP.NET, se presenti.

Direttiva Descrizione Esempio Equivalente Web Forms
@attribute Aggiunge un attributo a livello di classe al componente @attribute [Authorize] None
@code Aggiunge membri della classe al componente @code { ... } <script runat="server">...</script>
@implements Implementa l'interfaccia specificata @implements IDisposable Usare il code-behind
@inherits Eredita dalla classe di base specificata @inherits MyComponentBase <%@ Control Inherits="MyUserControlBase" %>
@inject Inserisce un servizio nel componente @inject IJSRuntime JS None
@layout Specifica un componente di layout per il componente @layout MainLayout <%@ Page MasterPageFile="~/Site.Master" %>
@namespace Imposta lo spazio dei nomi per il componente @namespace MyNamespace None
@page Specifica la route per il componente @page "/product/{id}" <%@ Page %>
@typeparam Specifica un parametro di tipo generico per il componente @typeparam TItem Usare il code-behind
@using Specifica uno spazio dei nomi da inserire nell'ambito @using MyComponentNamespace Aggiungere lo spazio dei nomi in web.config

Inoltre, i componenti Razor fanno ampiamente uso degli attributi di direttiva sugli elementi per controllare vari aspetti del modo in cui i componenti vengono compilati (gestione degli eventi, data binding, riferimenti ai componenti e così via). Tutti gli attributi della direttiva seguono una sintassi generica comune, in cui i valori tra parentesi sono facoltativi:

@directive(-suffix(:name))(="value")

La tabella seguente riepiloga i vari attributi per le direttive Razor usate in Blazor.

Attributo Descrizione Esempio
@attributes Esegue il rendering di un dizionario di attributi <input @attributes="ExtraAttributes" />
@bind Crea un data binding bidirezionale <input @bind="username" @bind:event="oninput" />
@on{event} Aggiunge un gestore eventi per l'evento specificato <button @onclick="IncrementCount">Click me!</button>
@key Specifica una chiave che l'algoritmo diffing deve utilizzare per conservare gli elementi in una raccolta <DetailsEditor @key="person" Details="person.Details" />
@ref Acquisisce un riferimento al componente o all'elemento HTML <MyDialog @ref="myDialog" />

I vari attributi di direttiva usati da Blazor (@onclick, @bind, @ref e così via) sono trattati nelle sezioni seguenti e nei capitoli successivi.

Molte delle sintassi usate nei file .aspx e .ascx hanno sintassi parallele in Razor. Di seguito è riportato un semplice confronto delle sintassi per Web Forms ASP.NET e Razor.

Funzionalità Moduli Web Sintassi Razor Sintassi
Direttive <%@ [directive] %> <%@ Page %> @[directive] @page
Blocchi di codice <% %> <% int x = 123; %> @{ } @{ int x = 123; }
Espressioni
(codificato in HTML)
<%: %> <%:DateTime.Now %> Implicit: @
Explicit: @()
@DateTime.Now
@(DateTime.Now)
Commenti <%-- --%> <%-- Commented --%> @* *@ @* Commented *@
Data binding <%# %> <%# Bind("Name") %> @bind <input @bind="username" />

Per aggiungere membri alla classe del componente Razor, usare la direttiva @code. Questa tecnica è simile all'uso di un blocco <script runat="server">...</script> in un controllo utente o una pagina Web Forms ASP.NET.

@code {
    int count = 0;

    void IncrementCount()
    {
        count++;
    }
}

Poiché Razor è basato su C#, deve essere compilato dall'interno di un progetto C# (.csproj). Non è possibile compilare file .razor da un progetto Visual Basic (.vbproj). È comunque possibile fare riferimento a progetti Visual Basic dal progetto Blazor. Vale anche il concetto opposto:

Per informazioni di riferimento sulla sintassi Razor completa, vedere Informazioni di riferimento sulla sintassi Razor per ASP.NET Core.

Usare i componenti

Oltre al normale codice HTML, i componenti possono usare anche altri componenti come parte della logica di rendering. La sintassi per l'uso di un componente in Razor è simile all'uso di un controllo utente in un'applicazione Web Forms ASP.NET. I componenti vengono specificati usando un tag di elemento che corrisponde al nome del tipo del componente. Ad esempio, è possibile aggiungere un componente Counter simile al seguente:

<Counter />

A differenza di Web Forms ASP.NET, i componenti in Blazor:

  • non usano un prefisso di elemento, ad esempio asp:.
  • Non richiedono la registrazione nella pagina o in web.config.

Si pensi ai componenti Razor come i tipi .NET, perché è esattamente quello che sono. Se viene referenziato l'assembly contenente il componente, allora il componente è utilizzabile. Per portare lo spazio dei nomi del componente nell'ambito, applicare la direttiva @using:

@using MyComponentLib

<Counter />

Come illustrato nei progetti Blazor predefiniti, è prassi comune inserire direttive @using in un file _Imports.razor in modo che vengano importati in tutti i file .razor nella stessa directory e nelle directory generate.

Se lo spazio dei nomi per un componente non è incluso nell'ambito, è possibile specificare un componente usando il nome completo del tipo, come è possibile in C#:

<MyComponentLib.Counter />

Modificare il titolo pagina dai componenti

Quando si creano app di tipo SPA, è prassi comune che parti di una pagina effettuino il reload senza ricaricare l'intera pagina. Anche in questo caso, può essere utile avere il titolo della modifica della pagina in base al componente che viene correntemente caricato. Tale operazione può essere eseguita includendo il tag <PageTitle> nella pagina Razor del componente:

@page "/"
<PageTitle>Home</PageTitle>

I contenuti di questo elemento possono essere dinamici, ad esempio mostrando il numero corrente di messaggi:

<PageTitle>@MessageCount messages</PageTitle>

Si noti che se più componenti di una pagina specifica includono tag <PageTitle>, verrà visualizzato solo l'ultimo elemento (poiché ognuno sovrascriverà quello precedente).

Parametri del componente

In Web Forms ASP.NET è possibile inviare parametri e dati ai controlli utilizzando le proprietà pubbliche. Queste proprietà possono essere impostate nel markup utilizzando attributi o impostati direttamente nel codice. I componenti Razor funzionano in modo simile, anche se per essere considerati parametri del componente, le proprietà devono essere contrassegnate con l'attributo [Parameter].

Il componente Counter seguente definisce un parametro componente denominato IncrementAmount che può essere utilizzato per specificare la quantità di cui Counter deve essere incrementato ogni volta che si fa clic sul pulsante.

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    int currentCount = 0;

    [Parameter]
    public int IncrementAmount { get; set; } = 1;

    void IncrementCount()
    {
        currentCount+=IncrementAmount;
    }
}

Per specificare un parametro componente in Blazor, usare un attributo come si farebbe in Web Forms ASP.NET:

<Counter IncrementAmount="10" />

Parametri della stringa di query

I componenti di Razor possono anche sfruttare i valori della stringa di query della pagina su cui vengono resi come fonte di parametri. Per abilitare questa operazione, aggiungere l'attributo [SupplyParameterFromQuery] al parametro. Per esempio, la seguente definizione di parametro otterrebbe il suo valore dalla richiesta nella forma ?IncBy=2:

[Parameter]
[SupplyParameterFromQuery(Name = "IncBy")]
public int IncrementAmount { get; set; } = 1;

Se non si fornisce un Name personalizzato nell'attributo [SupplyParameterFromQuery], per impostazione predefinita corrisponderà al nome della proprietà (IncrementAmount in questo caso).

Componenti e limiti di errore

Per impostazione predefinita, le app Blazor rileveranno eccezioni non gestite e visualizzeranno un messaggio di errore nella parte inferiore della pagina senza ulteriori dettagli. Per limitare le parti dell’applicazione interessate da un errore non gestito, ad esempio per limitare l'impatto a un singolo componente, è possibile eseguire il wrapping del tag <ErrorBoundary> intorno alle dichiarazioni dei componenti.

Per esempio, per proteggersi da eventuali eccezioni lanciate dal componente Counter, dichiararlo all'interno di un <ErrorBoundary> e facoltativamente specificare un messaggio da visualizzare in caso di eccezione:

<ErrorBoundary>
    <ChildContent>
        <Counter />
    </ChildContent>
    <ErrorContent>
        Oops! The counter isn't working right now; please try again later.
    </ErrorContent>
</ErrorBoundary>

Se non è necessario specificare il contenuto personalizzato dell’errore personalizzato, è sufficiente eseguire il wrapping diretto del componente:

<ErrorBoundary>
  <Counter />
</ErrorBoundary>

Se si verifica un'eccezione non gestita nel componente di cui è stato eseguito il wrapping, verrà visualizzato un messaggio predefinito che indica che si è verificato un errore.

Gestori eventi

Sia Web Forms ASP.NET che Blazor forniscono un modello di programmazione basato su eventi per la gestione degli eventi dell'interfaccia utente. Esempi di tali eventi includono clic sui pulsanti e input di testo. In Web Forms ASP.NET si usano i controlli server HTML per gestire gli eventi dell'interfaccia utente esposti dal DOM oppure è possibile gestire gli eventi esposti dai controlli del server Web. Gli eventi vengono visualizzati nel server tramite richieste post-back del modulo. Si consideri il seguente esempio di clic del pulsante di Web Forms:

Counter.ascx

<asp:Button ID="ClickMeButton" runat="server" Text="Click me!" OnClick="ClickMeButton_Click" />

Counter.ascx.cs

public partial class Counter : System.Web.UI.UserControl
{
    protected void ClickMeButton_Click(object sender, EventArgs e)
    {
        Console.WriteLine("The button was clicked!");
    }
}

In Blazor è possibile registrare i gestori per gli eventi dell'interfaccia utente DOM usando direttamente gli attributi di direttiva del modulo @on{event}. Il segnaposto {event} rappresenta il nome dell'evento. Ad esempio, è possibile ascoltare i clic sui pulsanti in questo modo:

<button @onclick="OnClick">Click me!</button>

@code {
    void OnClick()
    {
        Console.WriteLine("The button was clicked!");
    }
}

I gestori dell’evento possono accettare un argomento facoltativo specifico dell'evento per fornire altre informazioni sull'evento. Ad esempio, gli eventi del mouse possono accettare un argomento MouseEventArgs, ma non è obbligatorio.

<button @onclick="OnClick">Click me!</button>

@code {
    void OnClick(MouseEventArgs e)
    {
        Console.WriteLine($"Mouse clicked at {e.ScreenX}, {e.ScreenY}.");
    }
}

Anziché fare riferimento a un gruppo di metodi per un gestore eventi, è possibile usare un'espressione lambda. Un'espressione lambda consente di chiudere altri valori nell'ambito.

@foreach (var buttonLabel in buttonLabels)
{
    <button @onclick="() => Console.WriteLine($"The {buttonLabel} button was clicked!")">@buttonLabel</button>
}

I gestori eventi possono essere eseguiti in modo sincrono o asincrono. Ad esempio, il gestore eventi OnClick seguente viene eseguito in modo asincrono:

<button @onclick="OnClick">Click me!</button>

@code {
    async Task OnClick()
    {
        var result = await Http.GetAsync("api/values");
    }
}

Dopo aver gestito un evento, viene eseguito il rendering del componente per tenere conto di eventuali modifiche dello stato del componente. Con i gestori eventi asincroni, il rendering del componente viene eseguito immediatamente dopo il completamento dell'esecuzione del gestore. Il rendering del componente viene eseguito di nuovo dopo il completamento del Task asincrono. Questa modalità di esecuzione asincrona consente di eseguire il rendering di un'interfaccia utente appropriata mentre il Task asincrono è ancora in corso.

<button @onclick="ShowMessage">Get message</button>

@if (showMessage)
{
    @if (message == null)
    {
        <p><em>Loading...</em></p>
    }
    else
    {
        <p>The message is: @message</p>
    }
}

@code
{
    bool showMessage = false;
    string message;

    public async Task ShowMessage()
    {
        showMessage = true;
        message = await MessageService.GetMessageAsync();
    }
}

Inoltre i componenti possono definire i propri eventi definendo un parametro componente di tipo EventCallback<TValue>. I callback degli eventi supportano tutte le varianti dei gestori eventi dell'interfaccia utente DOM: argomenti facoltativi, sincroni o asincroni, gruppi di metodi o espressioni lambda.

<button class="btn btn-primary" @onclick="OnClick">Click me!</button>

@code {
    [Parameter]
    public EventCallback<MouseEventArgs> OnClick { get; set; }
}

Data binding

Blazor fornisce un meccanismo semplice per associare i dati da un componente dell'interfaccia utente allo stato del componente. Questo approccio differisce dalle funzionalità di Web Forms ASP.NET per l'associazione dei dati dalle origini dati ai controlli dell'interfaccia utente. La gestione dei dati provenienti da fonti diverse è trattata nella sezione Gestire i dati.

Per creare un data binding bidirezionale da un componente dell'interfaccia utente allo stato del componente, usare l'attributo della direttiva @bind. Nell'esempio seguente il valore della casella di controllo è associato al campo isChecked.

<input type="checkbox" @bind="isChecked" />

@code {
    bool isChecked;
}

Quando viene eseguito il rendering del componente, il valore della casella di controllo viene impostato sul valore del campo isChecked. Quando l'utente attiva/disattiva la casella di controllo, viene generato l'evento onchange e il campo isChecked viene impostato sul nuovo valore. La sintassi @bind in questo caso equivale al markup seguente:

<input value="@isChecked" @onchange="(UIChangeEventArgs e) => isChecked = e.Value" />

Per modificare l'evento usato per l'associazione usare l'attributo @bind:event.

<input @bind="text" @bind:event="oninput" />
<p>@text</p>

@code {
    string text;
}

I componenti possono anche supportare il data binding ai relativi parametri. Per associare i dati, definire un parametro di callback di eventi con lo stesso nome del parametro associabile. Il suffisso "Changed" (Modificato) viene aggiunto al nome.

PasswordBox.razor

Password: <input
    value="@Password"
    @oninput="OnPasswordChanged"
    type="@(showPassword ? "text" : "password")" />

<label><input type="checkbox" @bind="showPassword" />Show password</label>

@code {
    private bool showPassword;

    [Parameter]
    public string Password { get; set; }

    [Parameter]
    public EventCallback<string> PasswordChanged { get; set; }

    private Task OnPasswordChanged(ChangeEventArgs e)
    {
        Password = e.Value.ToString();
        return PasswordChanged.InvokeAsync(Password);
    }
}

Per concatenare un data binding a un elemento dell'interfaccia utente sottostante, impostare il valore e gestire l'evento direttamente sull'elemento dell'interfaccia utente, anziché usare l'attributo @bind.

Per eseguire l'associazione a un parametro del componente, usare un attributo @bind-{Parameter} per specificare il parametro a cui si desidera eseguire l'associazione.

<PasswordBox @bind-Password="password" />

@code {
    string password;
}

Modifiche stato

Se lo stato del componente è cambiato all'esterno di un normale evento dell'interfaccia utente o di un callback di eventi, il componente deve segnalare manualmente che deve essere eseguito di nuovo il rendering. Per segnalare che lo stato di un componente è cambiato, richiamare il metodo StateHasChanged sul componente.

Nell'esempio seguente un componente visualizza un messaggio da un servizio AppState che può essere aggiornato da altre parti dell'applicazione. Il componente registra il relativo metodo StateHasChanged con l'evento AppState.OnChange in modo che venga eseguito il rendering del componente ogni volta che il messaggio viene aggiornato.

public class AppState
{
    public string Message { get; }

    // Lets components receive change notifications
    public event Action OnChange;

    public void UpdateMessage(string message)
    {
        Message = message;
        NotifyStateChanged();
    }

    private void NotifyStateChanged() => OnChange?.Invoke();
}
@inject AppState AppState

<p>App message: @AppState.Message</p>

@code {
    protected override void OnInitialized()
    {
        AppState.OnChange += StateHasChanged
    }
}

Ciclo di vita dei componenti

Il framework Web Forms ASP.NET include metodi del ciclo di vita ben definiti per moduli, pagine e controlli. Ad esempio, il controllo seguente implementa i gestori di eventi per gli eventi del ciclo di vita Init, Load e UnLoad:

Counter.ascx.cs

public partial class Counter : System.Web.UI.UserControl
{
    protected void Page_Init(object sender, EventArgs e) { ... }
    protected void Page_Load(object sender, EventArgs e) { ... }
    protected void Page_UnLoad(object sender, EventArgs e) { ... }
}

Anche i componenti Razor hanno un ciclo di vita ben definito. Il ciclo di vita di un componente può essere usato per inizializzare lo stato del componente e implementare comportamenti avanzati dei componenti.

Tutti i metodi del ciclo di vita dei componenti Blazor hanno versioni sincrone e asincrone. Il rendering dei componenti è sincrono. Non è possibile eseguire la logica asincrona come parte del rendering del componente. Tutta la logica asincrona deve essere eseguita come parte di un metodo async del ciclo di vita.

OnInitialized

I metodi OnInitialized e OnInitializedAsync vengono utilizzati per inizializzare il componente. Un componente viene in genere inizializzato dopo il primo rendering. Dopo l'inizializzazione di un componente, il rendering può essere eseguito più volte prima di venire eliminato. Il metodo OnInitialized è simile all'evento Page_Load nelle pagine e nei controlli Web Forms ASP.NET.

protected override void OnInitialized() { ... }
protected override async Task OnInitializedAsync() { await ... }

OnParametersSet

I metodi OnParametersSet e OnParametersSetAsync vengono richiamati quando un componente ha ricevuto parametri dal relativo elemento padre e il valore viene assegnato alle proprietà. Questi metodi vengono eseguiti dopo l'inizializzazione del componente e ogni volta che viene eseguito il rendering del componente.

protected override void OnParametersSet() { ... }
protected override async Task OnParametersSetAsync() { await ... }

OnAfterRender

I metodi OnAfterRender e OnAfterRenderAsync vengono chiamati dopo il completamento del rendering di un componente. A questo punto vengono popolati i riferimenti agli elementi e ai componenti (per maggiori informazioni su questi concetti vedere più avanti). A questo punto viene abilitata l'interattività con il browser. Le interazioni con l'esecuzione DOM e JavaScript possono essere eseguite in modo sicuro.

protected override void OnAfterRender(bool firstRender)
{
    if (firstRender)
    {
        ...
    }
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        await ...
    }
}

OnAfterRender e OnAfterRenderAsyncnon vengono richiamati quando si esegue la pre-distribuzione nel server.

Il parametro firstRender è true la prima volta che viene eseguito il rendering del componente; in caso contrario, il relativo valore è false.

IDisposable

I componenti Razor possono implementare IDisposable per eliminare le risorse quando il componente viene rimosso dall'interfaccia utente. Un componente Razor può implementare IDispose usando la direttiva @implements:

@using System
@implements IDisposable

...

@code {
    public void Dispose()
    {
        ...
    }
}

Riferimenti ai componenti di acquisizione

In Web Forms ASP.NET è prassi comune modificare un'istanza di controllo direttamente nel codice facendo riferimento al relativo ID. In Blazor è possibile anche acquisire e modificare un riferimento a un componente, anche se si tratta di una prassi molto meno comune.

Per acquisire un riferimento a un componente in Blazor, usare l'attributo della direttiva @ref. Il valore dell'attributo deve corrispondere al nome di un campo impostabile con lo stesso tipo del componente a cui si fa riferimento.

<MyLoginDialog @ref="loginDialog" ... />

@code {
    MyLoginDialog loginDialog = default!;

    void OnSomething()
    {
        loginDialog.Show();
    }
}

Quando viene eseguito il rendering del componente genitore, il campo viene popolato con l'istanza del componente figlio. È quindi possibile richiamare metodi o manipolare in altro modo l'istanza del componente.

Non è consigliabile manipolare direttamente lo stato dei componenti usandone i riferimenti. In questo modo si impedisce il rendering automatico del componente nei momenti corretti.

Riferimenti agli elementi capture

I componenti Razor possono acquisire riferimenti a un elemento. A differenza dei controlli server HTML in Web Forms ASP.NET, non è possibile modificare direttamente il DOM usando un riferimento agli elementi in Blazor. Blazor gestisce la maggior parte delle interazioni DOM usando il relativo algoritmo di diffing DOM. I riferimenti agli elementi acquisiti in Blazor sono opachi. Tuttavia, vengono usati per passare un riferimento a un elemento specifico in una chiamata di interoperabilità JavaScript. Per altre informazioni sull'interoperabilità JavaScript, vedere interoperabilità JavaScript di ASP.NET CoreBlazor.

Componenti basati su modelli

In Web Forms ASP.NET è possibile creare controlli basati su modelli. I controlli basati su modelli consentono allo sviluppatore di specificare una parte del codice HTML usato per eseguire il rendering di un controllo contenitore. I meccanismi di creazione di controlli server basato su modelli sono complessi, ma consentono scenari avanzati per il rendering dei dati in modo personalizzabile dall'utente. Esempi di controlli basati su modelli includono Repeater e DataList.

I componenti Razor possono essere basati anche su modelli definendo i parametri del componente di tipo RenderFragment o RenderFragment<T>. Un RenderFragment rappresenta un blocco di markup Razor che può quindi essere sottoposto a rendering dal componente. Un RenderFragment<T> è un blocco di markup Razor che accetta un parametro specificabile quando viene eseguito il rendering del frammento di rendering.

Contenuto figlio

I componenti Razor possono acquisire il contenuto figlio come un RenderFragment ed eseguirne il rendering come parte del rendering del componente. Per acquisire il contenuto figlio, definire un parametro del componente di tipo RenderFragment e denominarlo ChildContent.

ChildContentComponent.razor

<h1>Component with child content</h1>

<div>@ChildContent</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get; set; }
}

Un componente padre può quindi fornire contenuto figlio utilizzando la normale sintassi Razor.

<ChildContentComponent>
    <ChildContent>
        <p>The time is @DateTime.Now</p>
    </ChildContent>
</ChildContentComponent>

Parametri del modello

Inoltre, un componente Razor basato su modelli può definire più parametri del componente di tipo RenderFragment o RenderFragment<T>. È possibile specificare il parametro per un RenderFragment<T> quando questi viene richiamato. Per specificare un parametro di tipo generico per un componente, usare la direttiva Razor @typeparam.

SimpleListView.razor

@typeparam TItem

@Heading

<ul>
@foreach (var item in Items)
{
    <li>@ItemTemplate(item)</li>
}
</ul>

@code {
    [Parameter]
    public RenderFragment Heading { get; set; }

    [Parameter]
    public RenderFragment<TItem> ItemTemplate { get; set; }

    [Parameter]
    public IEnumerable<TItem> Items { get; set; }
}

Quando si usa un componente basato su modelli, è possibile specificare i parametri del modello usando elementi figlio che corrispondono ai nomi dei parametri. Gli argomenti del componente di tipo RenderFragment<T> trasmessi come elementi hanno un parametro implicito denominato context. È possibile modificare il nome di questo parametro di implementazione usando l'attributo Context nell'elemento figlio. È possibile specificare qualsiasi parametro di tipo generico usando un attributo che corrisponde al nome del parametro di tipo generico. Se possibile, il parametro di tipo generico verrà dedotto:

<SimpleListView Items="messages" TItem="string">
    <Heading>
        <h1>My list</h1>
    </Heading>
    <ItemTemplate Context="message">
        <p>The message is: @message</p>
    </ItemTemplate>
</SimpleListView>

L'output di questo componente è simile al seguente:

<h1>My list</h1>
<ul>
    <li><p>The message is: message1</p></li>
    <li><p>The message is: message2</p></li>
<ul>

Code-behind

Un componente Razor viene in genere creato in un singolo file .razor. Tuttavia, è anche possibile separare il codice e il markup usando un file code-behind. Per usare un file componente, aggiungere un file C# che corrisponda al nome file del file componente, ma con l’aggiunta di un'estensione .cs (Counter.razor.cs). Usare il file C# per definire una classe di base per il componente. È possibile assegnare alla classe base qualsiasi nome desiderato, ma è comune assegnare alla classe lo stesso nome della classe componente, ma con l’aggiunta di un'estensione Base (CounterBase). Anche la classe basata su componenti deve derivare da ComponentBase. Quindi, nel file del componente Razor aggiungere la direttiva @inherits per specificare la classe di base per il componente (@inherits CounterBase).

Counter.razor

@inherits CounterBase

<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button @onclick="IncrementCount">Click me</button>

Counter.razor.cs

public class CounterBase : ComponentBase
{
    protected int currentCount = 0;

    protected void IncrementCount()
    {
        currentCount++;
    }
}

La visibilità dei membri del componente nella classe base deve essere protected o public perché risulti visibile alla classe del componente.

Risorse aggiuntive

Il precedente non è un trattamento esaustivo di tutti gli aspetti dei componenti Razor. Per altre informazioni su come Creare e usare componenti Razor ASP.NET Core, vedere la documentazione Blazor.