Razor componenti di ASP.NET Core

Questo articolo illustra come creare e usare Razor componenti nelle Blazor app, incluse indicazioni sulla sintassi, la Razor denominazione dei componenti, gli spazi dei nomi e i parametri dei componenti.

Blazor le app vengono compilate usando Razor componenti, noti in modo informale come Blazor componenti. Un componente è una parte autonoma dell'interfaccia utente con logica di elaborazione per abilitare il comportamento dinamico. I componenti possono essere annidati, riutilizzati, condivisi tra progetti e usati nelle app MVC e Razor Pages.

Classi di componenti

I componenti vengono implementati usando una combinazione di markup C# e HTML nei Razor file di componente con l'estensione di .razor file.

Sintassi Razor

I componenti usano Razor la sintassi. Due Razor funzionalità sono ampiamente usate da componenti, direttive e attributi di direttiva. Si tratta di parole chiave riservate precedute da @ che vengono visualizzate nel Razor markup:

  • Direttive: modificare la modalità di analisi o di funzioni del markup dei componenti. Ad esempio, la @page direttiva specifica un componente instradabile con un modello di route e può essere raggiunta direttamente dalla richiesta di un utente nel browser in corrispondenza di un URL specifico.
  • Attributi di direttiva: modificare la modalità di analisi o di funzioni di un elemento componente. Ad esempio, l'attributo @bind della direttiva per un <input> elemento associa i dati al valore dell'elemento.

Le direttive e gli attributi di direttiva usati nei componenti sono illustrati più avanti in questo articolo e in altri articoli del Blazor set di documentazione. Per informazioni generali sulla sintassi, vedere Razor informazioni di riferimento sullaRazor sintassi per ASP.NET Core.

Nomi

Il nome di un componente deve iniziare con un carattere maiuscolo:

  • ProductDetail.razor è valido.
  • productDetail.razor non è valido.

Le convenzioni di denominazione comuni Blazor usate in tutta la Blazor documentazione includono:

  • I percorsi dei file dei componenti usano il case Pascal† e vengono visualizzati prima di visualizzare esempi di codice del componente. I percorsi indicano i percorsi delle cartelle tipici. Ad esempio, Pages/ProductDetail.razor indica che il ProductDetail componente ha un nome file di ProductDetail.razor e risiede nella Pages cartella dell'app.
  • I percorsi dei file dei componenti per i componenti instradabili corrispondono agli URL con i trattini visualizzati per gli spazi tra le parole nel modello di route di un componente. Ad esempio, un componente con un ProductDetail modello di route (@page "/product-detail"/product-detail) viene richiesto in un browser in corrispondenza dell'URL /product-detailrelativo .

† Maiuscole e minuscole (maiuscole) è una convenzione di denominazione senza spazi e punteggiatura e con la prima lettera di ogni parola maiuscola, inclusa la prima parola.

Routing

Il routing in Blazor viene ottenuto fornendo un modello di route a ogni componente accessibile nell'app con una @page direttiva. Quando viene compilato un Razor file con una @page direttiva, alla classe generata viene assegnato un RouteAttribute valore che specifica il modello di route. In fase di esecuzione, il router cerca classi di componenti con un RouteAttribute e esegue il rendering di qualsiasi componente abbia un modello di route corrispondente all'URL richiesto.

Il componente seguente HelloWorld usa un modello di route di /hello-world. La pagina Web di cui è stato eseguito il rendering per il componente viene raggiunta all'URL /hello-worldrelativo . Quando si esegue un'app Blazor in locale con il protocollo, l'host e la porta predefiniti, il HelloWorld componente viene richiesto nel browser all'indirizzo https://localhost:5001/hello-world. I componenti che producono pagine Web si trovano in genere nella Pages cartella, ma è possibile usare qualsiasi cartella per contenere i componenti, inclusi all'interno di cartelle nidificate.

Pages/HelloWorld.razor:

@page "/hello-world"

<h1>Hello World!</h1>

Il componente precedente viene caricato nel browser indipendentemente /hello-world dal fatto che si aggiunga o meno il componente alla navigazione dell'interfaccia utente dell'app. Facoltativamente, i componenti possono essere aggiunti al NavMenu componente in modo che un collegamento al componente venga visualizzato nel riquadro di spostamento basato sull'interfaccia utente dell'app.

Per il componente precedente HelloWorld , è possibile aggiungere un NavLink componente al NavMenu componente nella Shared cartella . Per altre informazioni, incluse le descrizioni dei NavLink componenti e NavMenu , vedere ASP.NET Core Blazor routing e navigazione.

markup

L'interfaccia utente di un componente viene definita usando Razor la sintassi, costituita da Razor markup, C# e HTML. Quando un'app viene compilata, il markup HTML e la logica di rendering C# vengono convertiti in una classe componente. Il nome della classe generata corrisponde al nome del file.

I membri della classe componente sono definiti in uno o più @code blocchi. In @code blocchi, lo stato del componente viene specificato ed elaborato con C#:

  • Inizializzatori di proprietà e di campo.
  • Valori dei parametri degli argomenti passati dai componenti padre e parametri di route.
  • Metodi per la gestione degli eventi utente, gli eventi del ciclo di vita e la logica del componente personalizzata.

I membri del componente vengono usati nella logica di rendering usando espressioni C# che iniziano con il @ simbolo. Ad esempio, viene eseguito il rendering di un campo C# anteponendo @ al nome del campo. Il componente seguente Markup valuta ed esegue il rendering:

  • headingFontStyle per il valore font-style della proprietà CSS dell'elemento titolo.
  • headingText per il contenuto dell'elemento titolo.

Pages/Markup.razor:

@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}

Nota

Esempi in tutta la Blazor documentazione specificano il private modificatore di accesso per i membri privati. I membri privati hanno come ambito la classe di un componente. Tuttavia, C# presuppone che il private modificatore di accesso non sia presente quando non è presente alcun modificatore di accesso, quindi contrassegnare in modo esplicito i membri "private" nel proprio codice è facoltativo. Per altre informazioni sui modificatori di accesso, vedere Access Modifiers (Guida per programmatori C#).

Il Blazor framework elabora internamente un componente come albero di rendering, ovvero la combinazione del modello DOM (Document Object Model) di un componente e del modello CSSOM (Cascading Style Sheet Object Model). Dopo il rendering iniziale del componente, l'albero di rendering del componente viene rigenerato in risposta agli eventi. Blazor confronta la nuova struttura ad albero di rendering con l'albero di rendering precedente e applica eventuali modifiche al DOM del browser per la visualizzazione. Per altre informazioni, vedere rendering dei componenti ASP.NET CoreRazor.

I componenti sono classi C# comuni e possono essere posizionati in qualsiasi punto all'interno di un progetto. I componenti che producono pagine Web si trovano in genere nella Pages cartella . I componenti non di pagina vengono spesso inseriti nella Shared cartella o in una cartella personalizzata aggiunta al progetto.

Razor la sintassi per le strutture di controllo, le direttive e gli attributi della direttiva C# sono minuscoli (esempi: @if, @code, @bind). I nomi delle proprietà sono maiuscoli ,ad esempio @Body per LayoutComponentBase.Body.

I metodi asincroni (async) non supportano la restituzione void

Il Blazor framework non tiene traccia voiddei metodi asincroni (async). Di conseguenza, le eccezioni non vengono rilevate se void viene restituito. Restituisce sempre un Task oggetto da metodi asincroni.

Componenti annidati

I componenti possono includere altri componenti dichiarandoli usando la sintassi HTML. Il markup per l'uso di un componente è simile a un tag HTML, in cui il nome del tag è il tipo di componente.

Si consideri il componente seguente Heading , che può essere usato da altri componenti per visualizzare un'intestazione.

Shared/Heading.razor:

<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}

Il markup seguente nel componente esegue il rendering del HeadingExample componente precedente Heading nella posizione in cui viene visualizzato il <Heading /> tag.

Pages/HeadingExample.razor:

@page "/heading-example"

<Heading />

Se un componente contiene un elemento HTML con una prima lettera maiuscola che non corrisponde a un nome di componente all'interno dello stesso spazio dei nomi, viene generato un avviso che indica che l'elemento ha un nome imprevisto. L'aggiunta di una @using direttiva per lo spazio dei nomi del componente rende disponibile il componente, che risolve l'avviso. Per altre informazioni, vedere la sezione Spazi dei nomi .

L'esempio Heading di componente illustrato in questa sezione non ha una @page direttiva, quindi il Heading componente non è direttamente accessibile a un utente tramite una richiesta diretta nel browser. Tuttavia, qualsiasi componente con una @page direttiva può essere annidato in un altro componente. Se il Heading componente è stato accessibile direttamente includendo @page "/heading" nella parte superiore del Razor file, il rendering del componente verrà eseguito per le richieste del browser sia /heading in che /heading-examplein .

Spazi dei nomi

In genere, lo spazio dei nomi di un componente è derivato dallo spazio dei nomi radice dell'app e dal percorso (cartella) del componente all'interno dell'app. Se lo spazio dei nomi radice dell'app è BlazorSample e il Counter componente si trova nella Pages cartella :

  • Lo Counter spazio dei nomi del componente è BlazorSample.Pages.
  • Il nome completo del tipo del componente è BlazorSample.Pages.Counter.

Per le cartelle personalizzate che contengono componenti, aggiungere una @using direttiva al componente padre o al file dell'app _Imports.razor . L'esempio seguente rende disponibili i componenti nella Components cartella :

@using BlazorSample.Components

Nota

@usingle direttive nel _Imports.razor file vengono applicate solo ai file (.razor), non ai Razor file C# (.cs).

È anche possibile fare riferimento ai componenti usando i nomi completi, che non richiedono una @using direttiva. L'esempio seguente fa riferimento direttamente al ProductDetail componente nella Components cartella dell'app:

<BlazorSample.Components.ProductDetail />

Lo spazio dei nomi di un componente creato con Razor è basato sul seguente (in ordine di priorità):

  • Direttiva @namespace nel Razor markup del file , ad esempio @namespace BlazorSample.CustomNamespace.
  • Il progetto si trova nel file di RootNamespace progetto , ad esempio <RootNamespace>BlazorSample</RootNamespace>.
  • Nome del progetto, tratto dal nome del file di progetto (.csproj) e dal percorso dalla radice del progetto al componente. Ad esempio, il framework viene risolto {PROJECT ROOT}/Pages/Index.razor con uno spazio dei nomi di progetto di BlazorSample (BlazorSample.csproj) nello spazio dei nomi BlazorSample.Pages per il Index componente. {PROJECT ROOT} è il percorso radice del progetto. I componenti seguono le regole di associazione dei nomi C#. Per il componente in questo esempio, i componenti nell'ambito Index sono tutti i componenti:
    • Nella stessa cartella , Pages.
    • I componenti nella radice del progetto che non specificano in modo esplicito uno spazio dei nomi diverso.

I seguenti non sono supportati:

  • Qualifica global:: .
  • Importazione di componenti con istruzioni aliaste using . Ad esempio, @using Foo = Bar non è supportato.
  • Nomi parzialmente qualificati. Ad esempio, non è possibile aggiungere @using BlazorSample a un componente e quindi fare riferimento al NavMenu componente nella cartella dell'app Shared (Shared/NavMenu.razor) con <Shared.NavMenu></Shared.NavMenu>.

Supporto della classe parziale

I componenti vengono generati come classi parziali C# e vengono creati usando uno degli approcci seguenti:

  • Un singolo file contiene codice C# definito in uno o più @code blocchi, markup HTML e Razor markup. Blazor i modelli di progetto definiscono i relativi componenti usando questo approccio a file singolo.
  • HTML e Razor markup vengono inseriti in un Razor file (.razor). Il codice C# viene inserito in un file code-behind definito come classe parziale (.cs).

Nota

Un foglio di stile del componente che definisce stili specifici del componente è un file separato (.css). BlazorL'isolamento CSS viene descritto più avanti in ASP.NET Core Blazor isolamento CSS.

Nell'esempio seguente viene illustrato il componente predefinito Counter con un @code blocco in un'app generata da un Blazor modello di progetto. Il markup e il codice C# si trovano nello stesso file. Questo è l'approccio più comune adottato nella creazione di componenti.

Pages/Counter.razor:

@page "/counter"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

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

@code {
    private int currentCount = 0;

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

Il componente seguente Counter divide HTML e Razor markup dal codice C# usando un file code-behind con una classe parziale:

Pages/CounterPartialClass.razor:

@page "/counter-partial-class"

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p role="status">Current count: @currentCount</p>

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

Pages/CounterPartialClass.razor.cs:

namespace BlazorSample.Pages
{
    public partial class CounterPartialClass
    {
        private int currentCount = 0;

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

@usingle direttive nel _Imports.razor file vengono applicate solo ai file (), non ai Razor file C# (.razor.cs). Aggiungere spazi dei nomi a un file di classe parziale in base alle esigenze.

Spazi dei nomi tipici usati dai componenti:

using System.Net.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;

Gli spazi dei nomi tipici includono anche lo spazio dei nomi dell'app e lo spazio dei nomi corrispondente alla cartella dell'app Shared :

using BlazorSample;
using BlazorSample.Shared;

Specificare una classe di base

La @inherits direttiva viene usata per specificare una classe di base per un componente. Nell'esempio seguente viene illustrato come un componente può ereditare una classe di base per fornire le proprietà e i metodi del componente. La BlazorRocksBase classe di base deriva da ComponentBase.

Pages/BlazorRocks.razor:

@page "/blazor-rocks"
@inherits BlazorRocksBase

<h1>@BlazorRocksText</h1>

BlazorRocksBase.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample
{
    public class BlazorRocksBase : ComponentBase
    {
        public string BlazorRocksText { get; set; } =
            "Blazor rocks the browser!";
    }
}

Parametri del componente

I parametri del componente passano i dati ai componenti e vengono definiti usando proprietà C# pubbliche nella classe componente con l'attributo[Parameter] . Nell'esempio seguente viene passato un tipo di riferimento predefinito () e un tipo di riferimento definito dall'utente (System.StringPanelBody) come parametri componente.

PanelBody.cs:

public class PanelBody
{
    public string? Text { get; set; }
    public string? Style { get; set; }
}

Shared/ParameterChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}

Avviso

La fornitura di valori iniziali per i parametri del componente è supportata, ma non crea un componente che scrive nei propri parametri dopo il rendering del componente per la prima volta. Per altre informazioni, vedere la sezione Parametri sovrascritti di questo articolo.

I Title parametri ParameterChild del componente e Body vengono impostati dagli argomenti nel tag HTML che esegue il rendering dell'istanza del componente. Il componente seguente ParameterParent esegue il rendering di due ParameterChild componenti:

  • Il primo ParameterChild componente viene eseguito senza fornire argomenti di parametro.
  • Il secondo ParameterChild componente riceve i valori per Title e Body dal ParameterParent componente, che usa un'espressione C# esplicita per impostare i valori delle PanelBodyproprietà del componente.

Pages/ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

Il markup HTML sottoposto a rendering seguente dal ParameterParent componente mostra ParameterChild i valori predefiniti del componente quando il ParameterParent componente non fornisce i valori dei parametri del componente. Quando il ParameterParent componente fornisce i valori dei parametri del componente, sostituisce i ParameterChild valori predefiniti del componente.

Nota

Per chiarezza, le classi di stile CSS di cui è stato eseguito il rendering non sono visualizzate nel markup HTML sottoposto a rendering seguente.

<h1>Child component (without attribute values)</h1>

<div>
    <div>Set By Child</div>
    <div>Set by child.</div>
</div>

<h1>Child component (with attribute values)</h1>

<div>
    <div>Set by Parent</div>
    <div>Set by parent.</div>
</div>

Assegnare un campo, una proprietà o un risultato di un metodo a un parametro componente come valore dell'attributo HTML usandoRazor il simbolo riservato@. Il componente seguente ParameterParent2 visualizza quattro istanze del componente precedente ParameterChild e imposta i Title relativi valori di parametro su:

Il @ prefisso è necessario per i parametri stringa. In caso contrario, il framework presuppone che sia impostato un valore letterale stringa.

Al di fuori dei parametri stringa, è consigliabile usare l'uso @ del prefisso per nonliterali, anche quando non sono strettamente necessari.

Non è consigliabile usare il @ prefisso per i valori letterali (ad esempio, valori booleani), le parole chiave (ad esempio, this) o , ma nullè possibile scegliere di usarle se si desidera. Ad esempio, è insolito, IsFixed="@true" ma supportato.

Le virgolette relative ai valori degli attributi dei parametri sono facoltative nella maggior parte dei casi per la specifica HTML5. Ad esempio, Value=this è supportato anziché Value="this". È tuttavia consigliabile usare virgolette perché è più facile ricordare e ampiamente adottato in tecnologie basate sul Web.

In tutta la documentazione, esempi di codice:

  • Usare sempre virgolette. Esempio: Value="this".
  • I nonliterali usano sempre il @ prefisso, anche quando è facoltativo. Esempi: Title="@title", dove title è una variabile tipizzata di stringa. Count="@ct", dove ct è una variabile numerata.
  • Valori letterali, all'esterno delle Razor espressioni, evitare @sempre . Esempio: IsFixed="true".

Pages/ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

Nota

Quando si assegna un membro C# a un parametro del componente, assegnare il prefisso al membro con il @ simbolo e non digitare mai il prefisso dell'attributo HTML del parametro.

Versione corretta:

<ParameterChild Title="@title" />

Non corretto:

<ParameterChild @Title="title" />

A differenza delle Razor pagine (.cshtml), Blazor non è possibile eseguire operazioni asincrone in un'espressione durante il rendering di un Razor componente. Questo perché Blazor è progettato per il rendering di interfacce utente interattive. In un'interfaccia utente interattiva, lo schermo deve sempre visualizzare qualcosa, quindi non ha senso bloccare il flusso di rendering. Il lavoro asincrono viene invece eseguito durante uno degli eventi del ciclo di vita asincroni. Dopo ogni evento del ciclo di vita asincrono, il componente può nuovamente eseguire il rendering. La sintassi seguente Razornon è supportata:

<ParameterChild Title="@await ..." />

Il codice nell'esempio precedente genera un errore del compilatore quando l'app viene compilata:

L'operatore 'await' può essere usato solo all'interno di un metodo asincrono. Prendere in considerazione la contrassegnatura di questo metodo con il modificatore "asincrono" e la modifica del tipo restituito in 'Task'.

Per ottenere un valore per il Title parametro nell'esempio precedente in modo asincrono, il componente può usare l'evento del ciclo di vita, come illustrato nell'esempioOnInitializedAsync seguente:

<ParameterChild Title="@title" />

@code {
    private string? title;

    protected override async Task OnInitializedAsync()
    {
        title = await ...;
    }
}

Per altre informazioni, vedere ciclo di vita del componente ASP.NET CoreRazor.

L'uso di un'espressione esplicita Razor per concatenare il testo con un risultato dell'espressione per l'assegnazione a un parametro non è supportato. L'esempio seguente cerca di concatenare il testo "Set by " con il valore della proprietà di un oggetto. Anche se questa sintassi è supportata in una Razor pagina (.cshtml), non è valida per l'assegnazione al parametro del Title figlio in un componente. La sintassi seguente Razornon è supportata:

<ParameterChild Title="Set by @(panelData.Title)" />

Il codice nell'esempio precedente genera un errore del compilatore quando l'app viene compilata:

Gli attributi dei componenti non supportano contenuto complesso (C# misto e markup).

Per supportare l'assegnazione di un valore composto, usare un metodo, un campo o una proprietà. Nell'esempio seguente viene eseguita la concatenazione di "Set by " e il valore della proprietà di un oggetto nel metodo GetTitleC# :

Pages/ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

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

Avviso

La fornitura di valori iniziali per i parametri del componente è supportata, ma non crea un componente che scrive nei propri parametri dopo il rendering del componente per la prima volta. Per altre informazioni, vedere la sezione Parametri sovrascritti di questo articolo.

I parametri dei componenti devono essere dichiarati come proprietà automatiche, ovvero che non devono contenere logica personalizzata nelle funzioni get di accesso o set . Ad esempio, la proprietà seguente StartData è una proprietà automatica:

[Parameter]
public DateTime StartData { get; set; }

Non inserire la logica personalizzata nella funzione di accesso o set perché i parametri del componente sono puramente destinati all'uso get come canale per un componente padre per il flusso delle informazioni in un componente figlio. Se una set funzione di accesso di una proprietà del componente figlio contiene la logica che causa il rerendering del componente padre, i risultati di un ciclo di rendering infinito.

Per trasformare un valore di parametro ricevuto:

  • Lasciare la proprietà del parametro come proprietà automatica per rappresentare i dati non elaborati forniti.
  • Creare una proprietà o un metodo diverso per fornire i dati trasformati in base alla proprietà del parametro.

Eseguire l'override OnParametersSetAsync per trasformare un parametro ricevuto ogni volta che vengono ricevuti nuovi dati.

La scrittura di un valore iniziale in un parametro componente è supportata perché le assegnazioni di valori iniziali non interferiscono con il Blazorrendering automatico dei componenti. L'assegnazione seguente dell'oggetto locale DateTime corrente con DateTime.Now a StartData è una sintassi valida in un componente:

[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;

Dopo l'assegnazione iniziale di DateTime.Now, non assegnare un valore al StartData codice per sviluppatori. Per altre informazioni, vedere la sezione Parametri sovrascritti di questo articolo.

Applicare l'attributo[EditorRequired] per specificare un parametro componente obbligatorio. Se non viene fornito un valore di parametro, gli editor o gli strumenti di compilazione possono visualizzare avvisi all'utente. Questo attributo è valido solo nelle proprietà contrassegnate anche con l'attributo[Parameter] . L'oggetto EditorRequiredAttribute viene applicato in fase di progettazione e quando l'app viene compilata. L'attributo non viene applicato in fase di esecuzione e non garantisce unnull valore non parametro.

[Parameter]
[EditorRequired]
public string? Title { get; set; }

Sono supportati anche elenchi di attributi a riga singola:

[Parameter, EditorRequired]
public string? Title { get; set; }

Tuples (documentazione dell'API) sono supportati per i parametri e RenderFragment i tipi dei componenti. L'esempio di parametro del componente seguente passa tre valori in un Tupleoggetto :

Shared/RenderTupleChild.razor:

<div class="card w-50" style="margin-bottom:15px">
    <div class="card-header font-weight-bold"><code>Tuple</code> Card</div>
    <div class="card-body">
        <ul>
            <li>Integer: @Data?.Item1</li>
            <li>String: @Data?.Item2</li>
            <li>Boolean: @Data?.Item3</li>
        </ul>
    </div>
</div>

@code {
    [Parameter]
    public Tuple<int, string, bool>? Data { get; set; }
}

Pages/RenderTupleParent.razor:

@page "/render-tuple-parent"

<h1>Render <code>Tuple</code> Parent</h1>

<RenderTupleChild Data="@data" />

@code {
    private Tuple<int, string, bool> data = new(999, "I aim to misbehave.", true);
}

Solo le tupla senza nome sono supportate per C# 7.0 o versioni successive nei Razor componenti. Il supporto delle tuple denominate nei Razor componenti è previsto per una versione di ASP.NET Core futura. Per altre informazioni, vedere Blazor Problema di transpiler con nome Tuples (dotnet/aspnetcore #28982).

Virgolette ©2005 Universal Pictures: Serenity (Nathan Fillion)

Parametri di route

I componenti possono specificare parametri di route nel modello di route della @page direttiva. Il Blazor router usa parametri di route per popolare i parametri dei componenti corrispondenti.

I parametri di route facoltativi sono supportati. Nell'esempio seguente il text parametro facoltativo assegna il valore del segmento di route alla proprietà del Text componente. Se il segmento non è presente, il valore di è impostato su "fantastic" nel OnInitialized metodo del ciclo di Text vita.

Pages/RouteParameter.razor:

@page "/route-parameter/{text?}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string? Text { get; set; }

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

Per informazioni sui parametri di route catch-all ({*pageRoute}), che acquisiscono i percorsi tra più limiti di cartelle, vedere ASP.NET Core Blazor routing e spostamento.

Frammenti di rendering del contenuto figlio

I componenti possono impostare il contenuto di un altro componente. Il componente di assegnazione fornisce il contenuto tra i tag di apertura e chiusura del componente figlio.

Nell'esempio seguente il RenderFragmentChild componente ha un parametro componente che rappresenta un ChildContent segmento dell'interfaccia utente da eseguire come .RenderFragment La posizione del ChildContent markup del Razor componente è la posizione in cui viene eseguito il rendering del contenuto nell'output HTML finale.

Shared/RenderFragmentChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

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

Importante

La proprietà che riceve il RenderFragment contenuto deve essere denominata ChildContent per convenzione.

I callback degli eventi non sono supportati per RenderFragment.

Il componente seguente RenderFragmentParent fornisce contenuto per il rendering RenderFragmentChild del contenuto inserendo il contenuto all'interno dei tag di apertura e chiusura del componente figlio.

Pages/RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

A causa del Blazor rendering del contenuto figlio, i componenti di rendering all'interno di un for ciclo richiedono una variabile di indice locale se la variabile ciclo di incremento viene usata nel RenderFragmentChild contenuto del componente. L'esempio seguente può essere aggiunto al componente precedente RenderFragmentParent :

<h1>Three children with an index variable</h1>

@for (int c = 0; c < 3; c++)
{
    var current = c;

    <RenderFragmentChild>
        Count: @current
    </RenderFragmentChild>
}

In alternativa, usare un foreach ciclo con Enumerable.Range anziché un for ciclo. L'esempio seguente può essere aggiunto al componente precedente RenderFragmentParent :

<h1>Second example of three children with an index variable</h1>

@foreach (var c in Enumerable.Range(0,3))
{
    <RenderFragmentChild>
        Count: @c
    </RenderFragmentChild>
}

I frammenti di rendering vengono usati per eseguire il rendering del contenuto figlio in tutte Blazor le app e vengono descritti con esempi nelle sezioni di articoli e articoli seguenti:

Nota

BlazorI componenti predefiniti Razor di framework usano la stessa ChildContent convenzione dei parametri del componente per impostare il contenuto. È possibile visualizzare i componenti che impostano il contenuto figlio cercando il nome ChildContent della proprietà del parametro del componente nella documentazione dell'API (filtra l'API con il termine di ricerca "ChildContent").

Eseguire il rendering di frammenti per la logica di rendering riutilizzabile

È possibile considerare i componenti figlio puramente come modo per riutilizzare la logica di rendering. Nel blocco di qualsiasi componente definire un RenderFragment e eseguire il rendering del @code frammento da qualsiasi posizione quante volte necessario:

<h1>Hello, world!</h1>

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo = __builder =>
    {
        <p>Welcome to your new app!</p>
    };
}

Per altre informazioni, vedere Riutilizzare la logica di rendering.

Parametri sovrascritti

Il framework impone in genere l'assegnazione Blazor di parametri padre-a-figlio sicuri:

  • I parametri non vengono sovrascritti in modo imprevisto.
  • Gli effetti collaterali sono ridotti al minimo. Ad esempio, vengono evitati altri rendering perché possono creare cicli di rendering infinito.

Un componente figlio riceve nuovi valori di parametro che potrebbero sovrascrivere i valori esistenti quando il componente padre esegue di nuovo il render. La sovrascrittura accidentale dei valori dei parametri in un componente figlio spesso si verifica quando si sviluppa il componente con uno o più parametri associati a dati e lo sviluppatore scrive direttamente in un parametro nel componente figlio:

  • Il rendering del componente figlio viene eseguito con uno o più valori di parametro dal componente padre.
  • Il figlio scrive direttamente sul valore di un parametro.
  • Il componente padre rerenders e sovrascrive il valore del parametro figlio.

Il potenziale per sovrascrivere i valori dei parametri si estende anche nelle funzioni di accesso delle proprietà set del componente figlio.

Importante

Le linee guida generali non consentono di creare componenti che scrivono direttamente nei propri parametri dopo il rendering del componente per la prima volta.

Prendere in considerazione il componente seguente Expander :

  • Esegue il rendering del contenuto figlio.
  • Attiva l'interruttore che mostra il contenuto figlio con un parametro del componente (Expanded).

Dopo che il componente seguente Expander illustra un parametro sovrascritto, viene visualizzato un componente modificato Expander per illustrare l'approccio corretto per questo scenario. Gli esempi seguenti possono essere inseriti in un'app di esempio locale per sperimentare i comportamenti descritti.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>

        @if (Expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    [Parameter]
    public bool Expanded { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    private void Toggle()
    {
        Expanded = !Expanded;
    }
}

Il Expander componente viene aggiunto al componente padre seguente ExpanderExample che può chiamare StateHasChanged:

Pages/ExpanderExample.razor:

@page "/expander-example"

<Expander Expanded="true">
    Expander 1 content
</Expander>

<Expander Expanded="true" />

<button @onclick="StateHasChanged">
    Call StateHasChanged
</button>

Inizialmente, i componenti si comportano in modo indipendente quando le Expander proprietà Expanded vengono attivate. I componenti figlio mantengono gli stati previsti.

Se StateHasChanged viene chiamato in un componente padre, il Blazor framework rerendere i componenti figlio se i parametri potrebbero essere stati modificati:

  • Per un gruppo di tipi di parametri che Blazor controllano in modo esplicito, Blazor rerendere un componente figlio se rileva che uno dei parametri è stato modificato.
  • Per i tipi di parametri deselezionati, Blazor esegue nuovamente il ripristino del componente figlio indipendentemente dal fatto che i parametri siano stati modificati. Il contenuto figlio rientra in questa categoria di tipi di parametri perché il contenuto figlio è di tipo RenderFragment, che è un delegato che fa riferimento ad altri oggetti modificabili.

Per il ExpanderExample componente:

  • Il primo Expander componente imposta il contenuto figlio in un oggetto potenzialmente modificabile RenderFragment, quindi una chiamata a StateHasChanged nel componente padre rerendere automaticamente il componente e potenzialmente sovrascrive il valore di Expanded al relativo valore intitiale di true.
  • Il secondo Expander componente non imposta il contenuto figlio. Pertanto, un oggetto potenzialmente mutable RenderFragment non esiste. Una chiamata a StateHasChanged nel componente padre non esegue automaticamente il renderendere il componente figlio, pertanto il valore del Expanded componente non viene sovrascritto.

Per mantenere lo stato nello scenario precedente, usare un campo privato nel Expander componente per mantenere lo stato disattivato.

Componente modificato Expander seguente:

  • Accetta il valore del Expanded parametro del componente dall'elemento padre.
  • Assegna il valore del parametro del componente a un campo privato (expanded) nell'eventoOnInitialized.
  • Usa il campo privato per mantenere lo stato di interruttore interno, che illustra come evitare di scrivere direttamente in un parametro.

Nota

Il consiglio in questa sezione si estende alla logica simile nelle funzioni di accesso ai parametri set del componente, che possono causare effetti collaterali indesiderati simili.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>

        @if (expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    private bool expanded;

    [Parameter]
    public bool Expanded { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    protected override void OnInitialized()
    {
        expanded = Expanded;
    }

    private void Toggle()
    {
        expanded = !expanded;
    }
}

Per esempi di associazione padre-figlio bidirezionale, vedere ASP.NET Core Blazor data binding. Per altre informazioni, vedere Blazor Errore di associazione bidirezionale (dotnet/aspnetcore #24599).

Per altre informazioni sul rilevamento delle modifiche, informazioni sull'inserimento di informazioni sui tipi esatti che Blazor controllano, vedere rendering dei componenti ASP.NET CoreRazor.

Parametri splatting e arbitrari degli attributi

I componenti possono acquisire e eseguire il rendering di attributi aggiuntivi oltre ai parametri dichiarati del componente. Gli attributi aggiuntivi possono essere acquisiti in un dizionario e quindi splatted su un elemento quando il componente viene eseguito il rendering usando l'attributo @attributesRazor direttiva. Questo scenario è utile per definire un componente che produce un elemento di markup che supporta diverse personalizzazioni. Ad esempio, può essere noioso definire gli attributi separatamente per un <input> oggetto che supporta molti parametri.

Nel componente seguente Splat :

  • Il primo <input> elemento (id="useIndividualParams") usa i singoli parametri del componente.
  • Il secondo <input> elemento (id="useAttributesDict") usa lo splatting dell'attributo.

Pages/Splat.razor:

@page "/splat"

<input id="useIndividualParams"
       maxlength="@maxlength"
       placeholder="@placeholder"
       required="@required"
       size="@size" />

<input id="useAttributesDict"
       @attributes="InputAttributes" />

@code {
    private string maxlength = "10";
    private string placeholder = "Input placeholder text";
    private string required = "required";
    private string size = "50";

    private Dictionary<string, object> InputAttributes { get; set; } =
        new()
        {
            { "maxlength", "10" },
            { "placeholder", "Input placeholder text" },
            { "required", "required" },
            { "size", "50" }
        };
}

Gli elementi di cui è stato eseguito <input> il rendering nella pagina Web sono identici:

<input id="useIndividualParams"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

<input id="useAttributesDict"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

Per accettare attributi arbitrari, definire un parametro componente con la CaptureUnmatchedValues proprietà impostata su true:

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object>? InputAttributes { get; set; }
}

La CaptureUnmatchedValues proprietà su [Parameter] consente al parametro di corrispondere a tutti gli attributi che non corrispondono ad alcun altro parametro. Un componente può definire solo un singolo parametro con CaptureUnmatchedValues. Il tipo di proprietà usato con CaptureUnmatchedValues deve essere assegnabile da Dictionary<string, object> con chiavi di stringa. L'uso di IEnumerable<KeyValuePair<string, object>> o IReadOnlyDictionary<string, object> sono anche opzioni in questo scenario.

La posizione relativa alla posizione degli attributi dell'elemento @attributes è importante. Quando @attributes vengono splatted sull'elemento, gli attributi vengono elaborati da destra a sinistra (ultimo a primo). Si consideri l'esempio seguente di un componente padre che usa un componente figlio:

Shared/AttributeOrderChild1.razor:

<div @attributes="AdditionalAttributes" extra="5" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object>? AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent1.razor:

@page "/attribute-order-parent-1"

<AttributeOrderChild1 extra="10" />

L'attributo AttributeOrderChild1 del extra componente è impostato a destra di @attributes. Il AttributeOrderParent1 rendering del componente contiene extra="5" quando viene passato attraverso l'attributo aggiuntivo <div> perché gli attributi vengono elaborati a destra a sinistra (ultimo al primo):

<div extra="5" />

Nell'esempio seguente, l'ordine <div>di extra e @attributes viene invertito nel componente figlio :

Shared/AttributeOrderChild2.razor:

<div extra="5" @attributes="AdditionalAttributes" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object>? AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent2.razor:

@page "/attribute-order-parent-2"

<AttributeOrderChild2 extra="10" />

La <div> pagina Web di cui è stato eseguito il rendering del componente padre contiene extra="10" quando viene passato attraverso l'attributo aggiuntivo:

<div extra="10" />

Acquisire riferimenti ai componenti

I riferimenti ai componenti consentono di fare riferimento a un'istanza del componente per l'emissione di comandi. Per acquisire un riferimento al componente:

  • Aggiungere un @ref attributo al componente figlio.
  • Definire un campo con lo stesso tipo del componente figlio.

Quando viene eseguito il rendering del componente, il campo viene popolato con l'istanza del componente. È quindi possibile richiamare i metodi .NET nell'istanza.

Prendere in considerazione il componente seguente ReferenceChild che registra un messaggio quando ChildMethod viene chiamato.

Shared/ReferenceChild.razor:

@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> logger

@code {
    public void ChildMethod(int value)
    {
        logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}

Un riferimento al componente viene popolato solo dopo il rendering del componente e il relativo output include ReferenceChildl'elemento . Fino a quando non viene eseguito il rendering del componente, non c'è nulla da fare per fare riferimento.

Per modificare i riferimenti ai componenti al termine del rendering del componente, usare i OnAfterRender metodi oOnAfterRenderAsync.

Per usare una variabile di riferimento con un gestore eventi, usare un'espressione lambda o assegnare il delegato del gestore eventi nei OnAfterRender metodi oOnAfterRenderAsync. Ciò garantisce che la variabile di riferimento venga assegnata prima dell'assegnazione del gestore eventi.

L'approccio lambda seguente usa il componente precedente ReferenceChild .

Pages/ReferenceParent1.razor:

@page "/reference-parent-1"

<button @onclick="@(() => childComponent?.ChildMethod(5))">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild? childComponent;
}

L'approccio delegato seguente usa il componente precedente ReferenceChild .

Pages/ReferenceParent2.razor:

@page "/reference-parent-2"

<button @onclick="@(() => callChildMethod?.Invoke())">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild? childComponent;
    private Action? callChildMethod;

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            callChildMethod = CallChildMethod;
        }
    }

    private void CallChildMethod()
    {
        childComponent?.ChildMethod(5);
    }
}

Durante l'acquisizione dei riferimenti ai componenti, usare una sintassi simile per acquisire riferimenti agli elementi, l'acquisizione dei riferimenti ai componenti non è una funzionalità di interoperabilità JavaScript. I riferimenti ai componenti non vengono passati al codice JavaScript. I riferimenti ai componenti vengono usati solo nel codice .NET.

Importante

Non usare i riferimenti ai componenti per mutare lo stato dei componenti figlio. Usare invece i parametri dei componenti dichiarativi normali per passare i dati ai componenti figlio. L'uso dei parametri del componente comporta componenti figlio che rerendere automaticamente al momento corretto. Per altre informazioni, vedere la sezione parametri del componente e l'articolo ASP.NET Core Blazor data binding.

Contesto di sincronizzazione

Blazor usa un contesto di sincronizzazione (SynchronizationContext) per applicare un singolo thread logico di esecuzione. I metodi del ciclo di vita di un componente e i callback degli eventi generati da Blazor vengono eseguiti nel contesto di sincronizzazione.

Blazor ServerIl contesto di sincronizzazione tenta di emulare un ambiente a thread singolo in modo che corrisponda strettamente al modello WebAssembly nel browser, che è a thread singolo. In qualsiasi momento, il lavoro viene eseguito su un solo thread, che restituisce l'impressione di un singolo thread logico. Nessuna due operazioni vengono eseguite simultaneamente.

Evitare chiamate di blocco dei thread

In genere, non chiamare i metodi seguenti nei componenti. I metodi seguenti bloccano il thread di esecuzione e impediscono quindi all'app di riprendere il lavoro fino al completamento dell'oggetto sottostante Task :

Nota

Blazor gli esempi di documentazione che usano i metodi di blocco dei thread menzionati in questa sezione usano solo i metodi per scopi dimostrativi, non come indicazioni consigliate per la codifica. Ad esempio, alcune dimostrazioni di codice componente simulano un processo a esecuzione prolungata chiamando Thread.Sleep.

Richiamare i metodi componente esternamente per aggiornare lo stato

Se un componente deve essere aggiornato in base a un evento esterno, ad esempio un timer o un'altra notifica, usare il metodo, che invia l'esecuzione InvokeAsync del codice al Blazorcontesto di sincronizzazione. Si consideri, ad esempio, il servizio notificatore seguente che può inviare una notifica a qualsiasi componente in ascolto sullo stato aggiornato. Il Update metodo può essere chiamato da qualsiasi posizione nell'app.

TimerService.cs:

public class TimerService : IDisposable
{
    private int elapsedCount;
    private readonly static TimeSpan heartbeatTickRate = TimeSpan.FromSeconds(5);
    private readonly ILogger<TimerService> logger;
    private readonly NotifierService notifier;
    private PeriodicTimer? timer;

    public TimerService(NotifierService notifier,
        ILogger<TimerService> logger)
    {
        this.notifier = notifier;
        this.logger = logger;
    }

    public async Task Start()
    {
        if (timer is null)
        {
            timer = new(heartbeatTickRate);
            logger.LogInformation("Started");

            using (timer)
            {
                while (await timer.WaitForNextTickAsync())
                {
                    elapsedCount += 1;
                    await notifier.Update("elapsedCount", elapsedCount);
                    logger.LogInformation($"elapsedCount: {elapsedCount}");
                }
            }
        }
    }

    public void Dispose()
    {
        timer?.Dispose();
    }
}

NotifierService.cs:

public class NotifierService
{
    public async Task Update(string key, int value)
    {
        if (Notify != null)
        {
            await Notify.Invoke(key, value);
        }
    }

    public event Func<string, int, Task>? Notify;
}

Registrare i servizi:

  • In un'app Blazor WebAssembly registrare i servizi come singleton in Program.cs:

    builder.Services.AddSingleton<NotifierService>();
    builder.Services.AddSingleton<TimerService>();
    
  • In un'app Blazor Server registrare i servizi come ambiti in Program.cs:

    builder.Services.AddScoped<NotifierService>();
    builder.Services.AddScoped<TimerService>();
    

Usare l'oggetto NotifierService per aggiornare un componente.

Pages/ReceiveNotifications.razor:

@page "/receive-notifications"
@implements IDisposable
@inject NotifierService Notifier
@inject TimerService Timer

<h1>Receive Notifications</h1>

<h2>Timer Service</h2>

<button @onclick="StartTimer">Start Timer</button>

<h2>Notifications</h2>

<p>
    Status:
    @if (lastNotification.key is not null)
    {
        <span>@lastNotification.key = @lastNotification.value</span>
    }
    else
    {
        <span>Awaiting first notification</span>
    }
</p>

@code {
    private (string key, int value) lastNotification;

    protected override void OnInitialized()
    {
        Notifier.Notify += OnNotify;
    }

    public async Task OnNotify(string key, int value)
    {
        await InvokeAsync(() =>
        {
            lastNotification = (key, value);
            StateHasChanged();
        });
    }

    private async Task StartTimer()
    {
        await Timer.Start();
    }

    public void Dispose()
    {
        Notifier.Notify -= OnNotify;
    }
}

Nell'esempio precedente:

  • NotifierService richiama il metodo del OnNotify componente all'esterno del Blazorcontesto di sincronizzazione del componente. InvokeAsync viene usato per passare al contesto e alla coda corretti di un rendering. Per altre informazioni, vedere rendering dei componenti ASP.NET CoreRazor.
  • Il componente implementa IDisposable. Il OnNotify delegato viene annullato nel Dispose metodo, chiamato dal framework quando il componente viene eliminato. Per altre informazioni, vedere ciclo di vita del componente ASP.NET CoreRazor.

Usare @key per controllare la conservazione di elementi e componenti

Quando si esegue il rendering di un elenco di elementi o componenti e gli elementi o i componenti successivamente cambiano, Blazor è necessario decidere quali elementi o componenti precedenti possono essere conservati e come gli oggetti modello devono essere mappati a loro. In genere, questo processo è automatico e può essere ignorato, ma esistono casi in cui è possibile controllare il processo.

Prendere in considerazione i componenti e People i seguentiDetails:

  • Il Details componente riceve i dati (Data) dal componente padre People , che viene visualizzato in un <input> elemento. Qualsiasi elemento visualizzato specificato <input> può ricevere lo stato attivo della pagina dall'utente quando selezionano uno degli <input> elementi.
  • Il People componente crea un elenco di oggetti persona per la visualizzazione usando il Details componente. Ogni tre secondi, una nuova persona viene aggiunta alla raccolta.

Questa dimostrazione consente di:

  • Selezionare un <input> oggetto tra diversi componenti di cui è stato eseguito Details il rendering.
  • Studiare il comportamento dello stato attivo della pagina man mano che la raccolta persone aumenta automaticamente.

Shared/Details.razor:

<input value="@Data" />

@code {
    [Parameter]
    public string? Data { get; set; }
}

Nel componente seguente People ogni iterazione dell'aggiunta di una persona OnTimerCallback comporta la Blazor ricompilazione dell'intera raccolta. Lo stato attivo della pagina rimane sulla stessa posizione di indice degli <input> elementi, quindi lo stato attivo viene spostato ogni volta che viene aggiunta una persona. Spostando lo stato attivo lontano dal comportamento desiderato dell'utente selezionato. Dopo aver dimostrato il comportamento negativo con il componente seguente, l'attributo @key della direttiva viene usato per migliorare l'esperienza dell'utente.

Pages/People.razor:

@page "/people"
@using System.Timers
@implements IDisposable

@foreach (var person in people)
{
    <Details Data="@person.Data" />
}

@code {
    private Timer timer = new Timer(3000);

    public List<Person> people =
        new()
        {
            { new Person { Data = "Person 1" } },
            { new Person { Data = "Person 2" } },
            { new Person { Data = "Person 3" } }
        };

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            people.Insert(0,
                new Person
                {
                    Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
                });
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();

    public class Person
    {
        public string? Data { get; set; }
    }
}

Il contenuto della people raccolta viene modificato con voci inserite, eliminate o riordinate. Il rerendering può causare differenze di comportamento visibili. Ogni volta che una persona viene inserita nella raccolta, l'elemento precedente dell'elementopeople attualmente incentrato riceve lo stato attivo. Lo stato attivo dell'utente viene perso.

Il processo di mapping di elementi o componenti a una raccolta può essere controllato con l'attributo @key direttiva. L'uso di garantisce la conservazione di @key elementi o componenti in base al valore della chiave. Se il Details componente nell'esempio precedente viene chiaveto nell'elemento person , Blazor ignora i componenti di rerendering Details che non sono stati modificati.

Per modificare il People componente per usare l'attributo @key di direttiva con la people raccolta, aggiornare l'elemento <Details> al seguente:

<Details @key="person" Data="@person.Data" />

Quando la raccolta cambia, l'associazione people tra Details istanze e person istanze viene mantenuta. Quando un Person oggetto viene inserito all'inizio della raccolta, una nuova Details istanza viene inserita in tale posizione corrispondente. Altre istanze vengono lasciate invariate. Pertanto, lo stato attivo dell'utente non viene perso perché le persone vengono aggiunte alla raccolta.

Altri aggiornamenti della raccolta presentano lo stesso comportamento quando viene usato l'attributo @key direttiva:

  • Se un'istanza viene eliminata dalla raccolta, viene rimossa solo l'istanza del componente corrispondente dall'interfaccia utente. Altre istanze vengono lasciate invariate.
  • Se le voci della raccolta vengono riordinate, le istanze del componente corrispondenti vengono mantenute e riordinate nell'interfaccia utente.

Importante

Le chiavi sono locali per ogni elemento o componente del contenitore. Le chiavi non vengono confrontate a livello globale nel documento.

Quando usare @key

In genere, è opportuno usare @key ogni volta che viene eseguito il rendering di un elenco (ad esempio, in un foreach blocco) e esiste un valore appropriato per definire l'oggetto @key.

È anche possibile usare @key per mantenere un sottoalbero elemento o componente quando un oggetto non cambia, come illustrato negli esempi seguenti.

Esempio 1:

<li @key="person">
    <input value="@person.Data" />
</li>

Esempio 2:

<div @key="person">
    @* other HTML elements *@
</div>

Se un'istanza person viene modificata, la @key direttiva attribute impone Blazor di:

  • Eliminare l'intero <li> o <div> e i relativi discendenti.
  • Ricompilare il sottoalbero all'interno dell'interfaccia utente con nuovi elementi e componenti.

Ciò è utile per garantire che non venga mantenuto alcuno stato dell'interfaccia utente quando la raccolta cambia all'interno di un sottoalbero.

Ambito di @key

La @key direttiva attribute ha come ambito i propri elementi di pari livello all'interno del relativo elemento padre.

Si consideri l'esempio seguente. Le first chiavi e second vengono confrontate tra loro all'interno dello stesso ambito dell'elemento esterno <div> :

<div>
    <div @key="first">...</div>
    <div @key="second">...</div>
</div>

L'esempio seguente illustra first e second le chiavi nei propri ambiti, non correlate tra loro e senza influenza l'una sull'altra. Ogni @key ambito si applica solo al relativo elemento padre <div> , non tra gli elementi padre <div> :

<div>
    <div @key="first">...</div>
</div>
<div>
    <div @key="second">...</div>
</div>

Per il Details componente illustrato in precedenza, gli esempi seguenti eseguono il rendering person dei dati all'interno dello stesso @key ambito e illustrano i casi d'uso tipici per @key:

<div>
    @foreach (var person in people)
    {
        <Details @key="person" Data="@person.Data" />
    }
</div>
@foreach (var person in people)
{
    <div @key="person">
        <Details Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li @key="person">
            <Details Data="@person.Data" />
        </li>
    }
</ol>

Negli esempi seguenti viene eseguito solo l'ambito @key dell'elemento <div> o <li> che circonda ogni Details istanza del componente. Di conseguenza, person i dati per ogni membro della people raccolta non vengono chiaveti in ogni person istanza nei componenti di cui è stato Details eseguito il rendering. Evitare i modelli seguenti quando si usa @key:

@foreach (var person in people)
{
    <div>
        <Details @key="person" Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li>
            <Details @key="person" Data="@person.Data" />
        </li>
    }
</ol>

Quando non usare @key

Si verifica un costo delle prestazioni durante il rendering con @key. Il costo delle prestazioni non è elevato, ma specificare @key solo se mantenere l'elemento o il componente beneficia dell'app.

Anche se @key non viene usato, Blazor mantiene le istanze di elementi e componenti figlio il più possibile. L'unico vantaggio da usare @keyè controllare il modo in cui le istanze del modello vengono mappate alle istanze del Blazor componente mantenute, anziché selezionare il mapping.

Valori da usare per @key

In genere, è opportuno specificare uno dei valori seguenti per @key:

  • Istanze dell'oggetto modello. Ad esempio, l'istanza Person (person) è stata usata nell'esempio precedente. Ciò garantisce la conservazione in base all'uguaglianza dei riferimenti a oggetti.
  • Identificatori univoci. Ad esempio, gli identificatori univoci possono essere basati su valori di chiave primaria di tipo int, stringo Guid.

Assicurarsi che i valori usati per @key non si scontrano. Se vengono rilevati valori in conflitto all'interno dello stesso elemento padre, Blazor genera un'eccezione perché non può eseguire il mapping deterministico di elementi o componenti precedenti a nuovi elementi o componenti. Usare solo valori distinti, ad esempio istanze di oggetti o valori di chiave primaria.

Applicare un attributo

Gli attributi possono essere applicati ai componenti con la @attribute direttiva . L'esempio seguente applica l'attributo[Authorize] alla classe del componente:

@page "/"
@attribute [Authorize]

Attributi degli elementi HTML condizionali

Le proprietà degli attributi degli elementi HTML vengono impostate in modo condizionale in base al valore .NET. Se il valore è false o null, la proprietà non è impostata. Se il valore è true, la proprietà viene impostata.

Nell'esempio seguente determina IsCompleted se la <input> proprietà dell'elemento checked è impostata.

Pages/ConditionalAttribute.razor:

@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}

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

Avviso

Alcuni attributi HTML, ad esempio aria-pressed, non funzionano correttamente quando il tipo .NET è .bool In questi casi, usare un string tipo anziché un oggetto bool.

HTML non elaborato

Il rendering delle stringhe viene in genere eseguito usando nodi di testo DOM, il che significa che qualsiasi markup che può contenere viene ignorato e considerato come testo letterale. Per eseguire il rendering del codice HTML non elaborato, eseguire il wrapping del contenuto HTML in un MarkupString valore . Il valore viene analizzato come HTML o SVG e inserito nel DOM.

Avviso

Il rendering di codice HTML non elaborato costruito da qualsiasi origine non attendibile è un rischio per la sicurezza ed è sempre consigliabile evitare.

Nell'esempio seguente viene illustrato l'uso del MarkupString tipo per aggiungere un blocco di contenuto HTML statico all'output sottoposto a rendering di un componente.

Pages/MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

Razor Modelli

È possibile definire frammenti di rendering usando Razor la sintassi del modello per definire un frammento di interfaccia utente. Razor I modelli usano il formato seguente:

@<{HTML tag}>...</{HTML tag}>

Nell'esempio seguente viene illustrato come specificare RenderFragment e RenderFragment<TValue> valori ed eseguire il rendering dei modelli direttamente in un componente. I frammenti di rendering possono anche essere passati come argomenti ai componenti modello.

Pages/RazorTemplate.razor:

@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string? Name { get; set; }
    }
}

Output sottoposto a rendering del codice precedente:

<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>

Asset statici

Blazorsegue la convenzione di ASP.NET Core app per asset statici. Gli asset statici si trovano nella cartella o nelle cartelle wwwroot del web root progetto nellawwwroot cartella .

Usare un percorso relativo di base (/) per fare riferimento alla radice Web per un asset statico. Nell'esempio seguente, logo.png si trova fisicamente nella {PROJECT ROOT}/wwwroot/images cartella . {PROJECT ROOT} è la radice del progetto dell'app.

<img alt="Company logo" src="/images/logo.png" />

I componenti non supportano la notazione della barra tilde (~/).

Per informazioni sull'impostazione del percorso di base di un'app, vedere Ospitare e distribuire ASP.NET Core Blazor.

Gli helper tag non sono supportati nei componenti

Tag Helpers non sono supportati nei componenti. Per fornire funzionalità simili a tag in Blazor, creare un componente con la stessa funzionalità dell'helper tag e usare invece il componente .

Immagini SVG (Scalable Vector Graphics)

Poiché Blazor esegue il rendering di immagini HTML supportate dal browser, incluse le immagini SVG (Scalable Vector Graphics) (.svg), sono supportate tramite il <img> tag :

<img alt="Example image" src="image.svg" />

Analogamente, le immagini SVG sono supportate nelle regole CSS di un file del foglio di stile (.css):

.element-class {
    background-image: url("image.svg");
}

Blazor supporta l'elemento <foreignObject> per visualizzare codice HTML arbitrario all'interno di un svg. Il markup può rappresentare codice HTML arbitrario, un RenderFragmentoggetto o un Razor componente.

L'esempio seguente illustra:

  • Visualizzazione di un oggetto string (@message).
  • Associazione bidirezionale con un <input> elemento e un value campo.
  • Componente Robot .
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
    <rect x="0" y="0" rx="10" ry="10" width="200" height="200" stroke="black" 
        fill="none" />
    <foreignObject x="20" y="20" width="160" height="160">
        <p>@message</p>
    </foreignObject>
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject width="200" height="200">
        <label>
            Two-way binding:
            <input @bind="value" @bind:event="oninput" />
        </label>
    </foreignObject>
</svg>

<svg xmlns="http://www.w3.org/2000/svg">
    <foreignObject>
        <Robot />
    </foreignObject>
</svg>

@code {
    private string message = "Lorem ipsum dolor sit amet, consectetur adipiscing " +
        "elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";

    private string? value;
}

Comportamento di rendering degli spazi vuoti

A meno che la @preservewhitespace direttiva non venga usata con un valore , truelo spazio vuoto aggiuntivo viene rimosso per impostazione predefinita se:

  • Iniziali o finali all'interno di un elemento.
  • Iniziali o finali all'interno di un RenderFragment/RenderFragment<TValue> parametro (ad esempio, contenuto figlio passato a un altro componente).
  • Precede o segue un blocco di codice C#, ad esempio @if o @foreach.

La rimozione degli spazi vuoti può influire sull'output di cui è stato eseguito il rendering quando si usa una regola CSS, ad esempio white-space: pre. Per disabilitare questa ottimizzazione delle prestazioni e mantenere lo spazio vuoto, eseguire una delle azioni seguenti:

  • Aggiungere la @preservewhitespace true direttiva all'inizio del Razor file (.razor) per applicare la preferenza a un componente specifico.
  • Aggiungere la @preservewhitespace true direttiva all'interno di un _Imports.razor file per applicare la preferenza a una sottodirectory o all'intero progetto.

Nella maggior parte dei casi non è necessaria alcuna azione, perché le app continuano a comportarsi normalmente (ma più veloci). Se la rimozione degli spazi vuoti causa un problema di rendering per un determinato componente, usare @preservewhitespace true in tale componente per disabilitare questa ottimizzazione.

Supporto dei parametri di tipo generico

La @typeparam direttiva dichiara un parametro di tipo generico per la classe componente generata:

@typeparam TItem

La sintassi C# con where vincoli di tipo è supportata:

@typeparam TEntity where TEntity : IEntity

Nell'esempio seguente il ListGenericTypeItems1 componente viene tipizzato in modo generico come TExample.

Shared/ListGenericTypeItems1.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul>
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public IEnumerable<TExample>? ExampleList{ get; set; }
}

Il componente seguente GenericTypeExample1 esegue il rendering di due ListGenericTypeItems1 componenti:

  • I dati stringa o integer vengono assegnati al ExampleList parametro di ogni componente.
  • Il tipo string o int che corrisponde al tipo dei dati assegnati viene impostato per il parametro di tipo (TExample) di ogni componente.

Pages/GenericTypeExample1.razor:

@page "/generic-type-example-1"

<h1>Generic Type Example 1</h1>

<ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" 
                       TExample="string" />

<ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })" 
                       TExample="int" />

Per altre informazioni, vedere Razor informazioni di riferimento sulla sintassi per ASP.NET Core. Per un esempio di digitazione generica con componenti modelli, vedere ASP.NET Core Blazor componenti modelli.

Supporto del tipo generico a catena

Un componente predecessore può assegnare un parametro di tipo in base al nome per i discendenti usando l'attributo[CascadingTypeParameter] . Questo attributo consente a un'inferenza generica di tipo di usare automaticamente il parametro di tipo specificato con discendenti con un parametro di tipo con lo stesso nome.

Aggiungendo @attribute [CascadingTypeParameter(...)] a un componente, l'argomento di tipo generico specificato viene usato automaticamente dai discendenti che:

  • Vengono annidati come contenuto figlio per il componente nello stesso .razor documento.
  • Dichiarare anche un @typeparam con lo stesso nome.
  • Non avere un altro valore specificato in modo esplicito o implicito per il parametro di tipo. Se viene fornito o dedotto un altro valore, ha la precedenza sul tipo generico a catena.

Quando si riceve un parametro di tipo a catena, i componenti ottengono il valore del parametro dal predecessore più vicino con un CascadingTypeParameterAttribute nome corrispondente. I parametri di tipo generici a catena vengono sottoposti a override all'interno di un determinato sottoalbero.

La corrispondenza viene eseguita solo per nome. Pertanto, è consigliabile evitare un parametro di tipo generico a catena con un nome generico, ad esempio T o TItem. Se uno sviluppatore opta per l'inserimento a catena di un parametro di tipo, è implicitamente promettente che il suo nome sia abbastanza univoco non per scontrarsi con altri parametri di tipo a catena da componenti non correlati.

I tipi generici possono essere a catena ai componenti figlio in uno degli approcci seguenti con i componenti predecessori (padre), illustrati nelle due sezioni secondarie seguenti:

  • Impostare in modo esplicito il tipo generico a catena.
  • Inferire il tipo generico a catena.

Le sottosezioni seguenti forniscono esempi degli approcci precedenti usando i due ListDisplay componenti seguenti. I componenti ricevono e eseguono il rendering dei dati dell'elenco e vengono tipizzata in modo generico come TExample. Questi componenti sono per scopi dimostrativi e differiscono solo nel colore del testo che viene eseguito il rendering dell'elenco. Se si desidera sperimentare i componenti nelle sezioni secondarie seguenti in un'app di test locale, aggiungere prima i due componenti seguenti all'app.

Shared/ListDisplay1.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul style="color:blue">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public IEnumerable<TExample>? ExampleList { get; set; }
}

Shared/ListDisplay2.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul style="color:red">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public IEnumerable<TExample>? ExampleList { get; set; }
}

Tipi generici espliciti basati sui componenti predecessori

La dimostrazione in questa sezione si estende in modo esplicito a un tipo per TExample.

Nota

Questa sezione usa i due ListDisplay componenti nella sezione Supporto dei tipi generici cascaded .

Il componente seguente ListGenericTypeItems2 riceve dati e cascade un parametro di tipo generico denominato TExample ai relativi componenti discendenti. Nel componente padre successivo, il ListGenericTypeItems2 componente viene usato per visualizzare i dati dell'elenco con il componente precedente ListDisplay .

Shared/ListGenericTypeItems2.razor:

@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample

<h2>List Generic Type Items 2</h2>

@ChildContent

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

Il componente padre seguente GenericTypeExample2 imposta il contenuto figlio () di due ListGenericTypeItems2 componenti che specificano i ListGenericTypeItems2 tipi (RenderFragmentTExample), che sono a catena ai componenti figlio. ListDisplay i componenti vengono sottoposti a rendering con i dati dell'elemento di elenco visualizzati nell'esempio. I dati stringa vengono usati con il primo ListGenericTypeItems2 componente e i dati integer vengono usati con il secondo ListGenericTypeItems2 componente.

Pages/GenericTypeExample2.razor:

@page "/generic-type-example-2"

<h1>Generic Type Example 2</h1>

<ListGenericTypeItems2 TExample="string">
    <ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
    <ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
</ListGenericTypeItems2>

<ListGenericTypeItems2 TExample="int">
    <ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" />
    <ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" />
</ListGenericTypeItems2>

Specificando in modo esplicito il tipo consente anche l'uso di valori e parametri a catena per fornire dati ai componenti figlio, come illustrato nella dimostrazione seguente.

Shared/ListDisplay3.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul style="color:blue">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [CascadingParameter]
    protected IEnumerable<TExample>? ExampleList { get; set; }
}

Shared/ListDisplay4.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul style="color:red">
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [CascadingParameter]
    protected IEnumerable<TExample>? ExampleList { get; set; }
}

Shared/ListGenericTypeItems3.razor:

@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample

<h2>List Generic Type Items 3</h2>

@ChildContent

@if (ExampleList is not null)
{
    <ul style="color:green">
        @foreach(var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>

    <p>
        Type of <code>TExample</code>: @typeof(TExample)
    </p>
}

@code {
    [CascadingParameter]
    protected IEnumerable<TExample>? ExampleList { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}

Quando si escludono i dati nell'esempio ListGenericTypeItems3 seguente, il tipo deve essere fornito al componente.

Pages/GenericTypeExample3.razor:

@page "/generic-type-example-3"

<h1>Generic Type Example 3</h1>

<CascadingValue Value="@stringData">
    <ListGenericTypeItems3 TExample="string">
        <ListDisplay3 />
        <ListDisplay4 />
    </ListGenericTypeItems3>
</CascadingValue>

<CascadingValue Value="@integerData">
    <ListGenericTypeItems3 TExample="int">
        <ListDisplay3 />
        <ListDisplay4 />
    </ListGenericTypeItems3>
</CascadingValue>

@code {
    private List<string> stringData = new() { "Item 1", "Item 2" };
    private List<int> integerData = new() { 1, 2, 3 };
}

Quando più tipi generici sono a catena, è necessario passare i valori per tutti i tipi generici nel set. Nell'esempio seguente, TItem, TValuee TEdit sono GridColumn tipi generici, ma il componente padre che inserisce GridColumn non specifica il TItem tipo:

<GridColumn TValue="string" TEdit="@TextEdit" />

L'esempio GridColumn precedente genera un errore di compilazione in fase di compilazione mancante del TItem parametro di tipo. Il codice valido specifica tutti i tipi:

<GridColumn TValue="string" TEdit="@TextEdit" TItem="@User" />

Dedurre tipi generici basati sui componenti predecessori

La dimostrazione in questa sezione si estende a cascata su un tipo dedotto per TExample.

Nota

Questa sezione usa i due ListDisplay componenti nella sezione Supporto dei tipi generici cascaded .

Shared/ListGenericTypeItems4.razor:

@attribute [CascadingTypeParameter(nameof(TExample))]
@typeparam TExample

<h2>List Generic Type Items 4</h2>

@ChildContent

@if (ExampleList is not null)
{
    <ul style="color:green">
        @foreach(var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>

    <p>
        Type of <code>TExample</code>: @typeof(TExample)
    </p>
}

@code {
    [Parameter]
    public IEnumerable<TExample>? ExampleList { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }
}

Il componente seguente GenericTypeExample4 con tipi a catena dedotti fornisce dati diversi per la visualizzazione.

Pages/GenericTypeExample4.razor:

@page "/generic-type-example-4"

<h1>Generic Type Example 4</h1>

<ListGenericTypeItems4 ExampleList="@(new List<string> { "Item 5", "Item 6" })">
    <ListDisplay1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" />
    <ListDisplay2 ExampleList="@(new List<string> { "Item 3", "Item 4" })" />
</ListGenericTypeItems4>

<ListGenericTypeItems4 ExampleList="@(new List<int> { 7, 8, 9 })">
    <ListDisplay1 ExampleList="@(new List<int> { 1, 2, 3 })" />
    <ListDisplay2 ExampleList="@(new List<int> { 4, 5, 6 })" />
</ListGenericTypeItems4>

Il componente seguente GenericTypeExample5 con tipi a catena posticipati fornisce gli stessi dati per la visualizzazione. L'esempio seguente assegna direttamente i dati ai componenti.

Pages/GenericTypeExample5.razor:

@page "/generic-type-example-5"

<h1>Generic Type Example 5</h1>

<ListGenericTypeItems4 ExampleList="@stringData">
    <ListDisplay1 ExampleList="@stringData" />
    <ListDisplay2 ExampleList="@stringData" />
</ListGenericTypeItems4>

<ListGenericTypeItems4 ExampleList="@integerData">
    <ListDisplay1 ExampleList="@integerData" />
    <ListDisplay2 ExampleList="@integerData" />
</ListGenericTypeItems4>

@code {
    private List<string> stringData = new() { "Item 1", "Item 2" };
    private List<int> integerData = new() { 1, 2, 3 };
}

Eseguire il rendering Razor dei componenti da JavaScript

Razor i componenti possono essere sottoposti a rendering dinamico da JavaScript (JS) per le app esistenti JS .

Per eseguire il rendering di un Razor componente da JS, registrare il componente come componente radice per JS il rendering e assegnare il componente un identificatore:

  • In un'app Blazor Server modificare la chiamata a AddServerSideBlazor in Program.cs:

    builder.Services.AddServerSideBlazor(options =>
    {
        options.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
    });
    

    Nota

    L'esempio di codice precedente richiede uno spazio dei nomi per i componenti dell'app ,ad esempio , using BlazorSample.Pages;nel Program.cs file.

  • In un'app Blazor WebAssembly chiamare RegisterForJavaScriptRootComponents in Program.cs:

    builder.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
    

    Nota

    L'esempio di codice precedente richiede uno spazio dei nomi per i componenti dell'app ,ad esempio , using BlazorSample.Pages;nel Program.cs file.

Caricare Blazor nell'app JS (blazor.server.js o blazor.webassembly.js). Eseguire il rendering del componente da JS un elemento contenitore usando l'identificatore registrato, passando i parametri del componente in base alle esigenze:

let containerElement = document.getElementById('my-counter');
await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 });

Blazor elementi personalizzati

Il supporto sperimentale è disponibile per la creazione di elementi personalizzati usando il Microsoft.AspNetCore.Components.CustomElements pacchetto NuGet. Gli elementi personalizzati usano interfacce HTML standard per implementare elementi HTML personalizzati.

Avviso

Le funzionalità sperimentali vengono fornite allo scopo di esplorare la funzionalità valida e potrebbero non essere fornite in una versione stabile.

Registrare un componente radice come elemento personalizzato:

  • In un'app Blazor Server modificare la chiamata a AddServerSideBlazor in Program.cs:

    builder.Services.AddServerSideBlazor(options =>
    {
        options.RootComponents.RegisterAsCustomElement<Counter>("my-counter");
    });
    

    Nota

    L'esempio di codice precedente richiede uno spazio dei nomi per i componenti dell'app ,ad esempio , using BlazorSample.Pages;nel Program.cs file.

  • In un'app Blazor WebAssembly chiamare RegisterAsCustomElementRootComponents in Program.cs:

    builder.RootComponents.RegisterAsCustomElement<Counter>("my-counter");
    

    Nota

    L'esempio di codice precedente richiede uno spazio dei nomi per i componenti dell'app ,ad esempio , using BlazorSample.Pages;nel Program.cs file.

Includere il tag seguente <script> nel codice HTML dell'app prima del Blazor tag script:

<script src="/_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script>

Usare l'elemento personalizzato con qualsiasi framework Web. Ad esempio, l'elemento personalizzato del contatore precedente viene usato in un'app React con il markup seguente:

<my-counter increment-amount={incrementAmount}></my-counter>

Per un esempio completo di come creare elementi personalizzati con Blazor, vedere il Blazor progetto di esempio Di elementi personalizzati.

Avviso

La funzionalità degli elementi personalizzati è attualmente sperimentale, non supportata e soggetta a modifica o rimozione in qualsiasi momento. I vostri commenti e suggerimenti su come questo particolare approccio soddisfi i vostri requisiti.

Generare Angular e componenti React

Generare componenti JavaScript (JS) specifici del framework da Razor componenti per framework Web, ad esempio Angular o React. Questa funzionalità non è inclusa in .NET 6, ma è abilitata dal nuovo supporto per i componenti di rendering Razor da JS. L'esempio di generazione dei componenti in GitHub illustra come generare Angular e React componenti dai componenti.JSRazor Per altre informazioni, vedere il file dell'app di README.md esempio gitHub.

Avviso

Le funzionalità del componente Angular e React sono attualmente sperimentali, non supportate e soggette a modifiche o essere rimosse in qualsiasi momento. I vostri commenti e suggerimenti su come questo particolare approccio soddisfi i vostri requisiti.

Blazor le app vengono compilate usando Razor componenti, noti in modo informale come Blazor componenti. Un componente è una parte autonoma dell'interfaccia utente con la logica di elaborazione per abilitare il comportamento dinamico. I componenti possono essere annidati, riutilizzati, condivisi tra progetti e usati nelle app MVC e Razor Pages.

Classi componente

I componenti vengono implementati usando una combinazione di markup C# e HTML nei Razor file di componente con l'estensione del .razor file.

Sintassi Razor

I componenti usano Razor la sintassi. Due Razor funzionalità sono ampiamente usate da componenti, direttive e attributi di direttiva. Queste sono parole chiave riservate con prefisso @ che vengono visualizzate nel Razor markup:

  • Direttive: modificare il modo in cui il markup del componente viene analizzato o funzioni. Ad esempio, la @page direttiva specifica un componente instradabile con un modello di route e può essere raggiunto direttamente dalla richiesta di un utente nel browser in un URL specifico.
  • Attributi di direttiva: modificare il modo in cui un elemento componente viene analizzato o funzioni. Ad esempio, l'attributo @bind della direttiva per un <input> elemento associa i dati al valore dell'elemento.

Gli attributi delle direttive e delle direttive usati nei componenti sono illustrati ulteriormente in questo articolo e altri articoli del Blazor set di documentazione. Per informazioni generali sulla sintassi, vedere Razor informazioni di riferimento sullaRazor sintassi per ASP.NET Core.

Nomi

Il nome di un componente deve iniziare con un carattere maiuscolo:

  • ProductDetail.razor è valido.
  • productDetail.razor non è valido.

Le convenzioni di denominazione comuni Blazor usate in tutta la Blazor documentazione includono:

  • I percorsi dei file del componente usano case Pascal† e vengono visualizzati prima di visualizzare esempi di codice componente. I percorsi indicano percorsi di cartelle tipici. Ad esempio, Pages/ProductDetail.razor indica che il ProductDetail componente ha un nome file di ProductDetail.razor e risiede nella Pages cartella dell'app.
  • I percorsi dei file dei componenti per i componenti instradabili corrispondono agli URL con trattini visualizzati per gli spazi tra le parole nel modello di route di un componente. Ad esempio, un componente con un ProductDetail modello di route di /product-detail (@page "/product-detail") viene richiesto in un browser all'URL /product-detailrelativo .

†Pascal case (maiuscolo) è una convenzione di denominazione senza spazi e punteggiatura e con la prima lettera di ogni parola maiuscola, inclusa la prima parola.

Routing

Il routing in Blazor viene ottenuto fornendo un modello di route a ogni componente accessibile nell'app con una @page direttiva. Quando viene compilato un Razor file con una direttiva, la classe generata viene assegnata a una @pageRouteAttribute specifica del modello di route. In fase di esecuzione, il router cerca classi di componenti con un RouteAttribute componente e esegue il rendering di un modello di route corrispondente all'URL richiesto.

Il componente seguente HelloWorld usa un modello di route di /hello-world. La pagina Web di cui è stato eseguito il rendering per il componente viene raggiunta all'URL /hello-worldrelativo. Quando si esegue un'app Blazor in locale con il protocollo, l'host e la porta predefiniti, il HelloWorld componente viene richiesto nel browser in https://localhost:5001/hello-world. I componenti che producono pagine Web si trovano in genere nella cartella, ma è possibile usare qualsiasi cartella per contenere componenti, inclusi all'interno Pages di cartelle annidate.

Pages/HelloWorld.razor:

@page "/hello-world"

<h1>Hello World!</h1>

Il componente precedente viene caricato nel browser /hello-world indipendentemente dal fatto che si aggiunge o meno il componente allo spostamento dell'interfaccia utente dell'app. Facoltativamente, i componenti possono essere aggiunti al NavMenu componente in modo che un collegamento al componente venga visualizzato nello spostamento basato sull'interfaccia utente dell'app.

Per il componente precedente HelloWorld , è possibile aggiungere un NavLink componente al NavMenu componente nella Shared cartella. Per altre informazioni, incluse le descrizioni dei NavLink componenti e, vedere ASP.NET Core Blazor routing eNavMenu spostamento.

markup

L'interfaccia utente di un componente viene definita usando Razor la sintassi, costituita da Razor markup, C#e HTML. Quando viene compilata un'app, la logica di rendering HTML e C# viene convertita in una classe componente. Il nome della classe generata corrisponde al nome del file.

I membri della classe componente vengono definiti in uno o più @code blocchi. In @code blocchi, lo stato del componente viene specificato ed elaborato con C#:

  • Inizializzatori di proprietà e campi.
  • Valori dei parametri degli argomenti passati dai componenti padre e dai parametri di route.
  • Metodi per la gestione degli eventi utente, gli eventi del ciclo di vita e la logica dei componenti personalizzati.

I membri del componente vengono usati nella logica di rendering usando espressioni C# che iniziano con il @ simbolo. Ad esempio, un campo C# viene eseguito tramite prefisso @ al nome del campo. Il componente seguente Markup valuta e esegue il rendering:

  • headingFontStyle per il valore font-style della proprietà CSS dell'elemento intestazione.
  • headingText per il contenuto dell'elemento intestazione.

Pages/Markup.razor:

@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}

Nota

Esempi in tutta la Blazor documentazione specificano il private modificatore di accesso per i membri privati. I membri privati sono con ambito alla classe di un componente. Tuttavia, C# presuppone che il private modificatore di accesso non sia presente quando non è presente alcun modificatore di accesso, quindi contrassegnare in modo esplicito i membri "private" nel proprio codice è facoltativo. Per altre informazioni sui modificatori di accesso, vedere Access Modificatori (Guida per programmatori C#).

Il Blazor framework elabora un componente internamente come albero di rendering, ovvero la combinazione del modello a oggetti a oggetti document (DOM) di un componente e del modello a oggetti foglio di stile a cascata (CSSOM). Dopo il rendering iniziale del componente, l'albero di rendering del componente viene rigenerato in risposta agli eventi. Blazor confronta il nuovo albero di rendering rispetto all'albero di rendering precedente e applica eventuali modifiche al DOM del browser per la visualizzazione. Per altre informazioni, vedere rendering dei componenti ASP.NET CoreRazor.

I componenti sono classi C# normali e possono essere posizionati ovunque all'interno di un progetto. I componenti che producono pagine Web si trovano in genere nella Pages cartella. I componenti non di pagina vengono spesso inseriti nella Shared cartella o in una cartella personalizzata aggiunta al progetto.

Razor sintassi per strutture di controllo C#, direttive e attributi di direttiva sono minuscoli (esempi: @if, @code, @bind). I nomi delle proprietà sono maiuscole (ad esempio: @Body per LayoutComponentBase.Body).

I metodi asincroni (async) non supportano la restituzione void

Il Blazor framework non tiene traccia voiddei metodi asincroni (async). Di conseguenza, le eccezioni non vengono rilevate se void vengono restituite. Restituisce sempre un oggetto Task dai metodi asincroni.

Componenti annidati

I componenti possono includere altri componenti dichiarandoli usando la sintassi HTML. Il markup per l'uso di un componente è simile a un tag HTML, in cui il nome del tag è il tipo di componente.

Prendere in considerazione il componente seguente Heading , che può essere usato da altri componenti per visualizzare un'intestazione.

Shared/Heading.razor:

<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}

Il markup seguente nel HeadingExample componente esegue il rendering del componente precedente Heading nella posizione in cui viene visualizzato il <Heading /> tag.

Pages/HeadingExample.razor:

@page "/heading-example"

<Heading />

Se un componente contiene un elemento HTML con una prima lettera maiuscola che non corrisponde a un nome del componente all'interno dello stesso spazio dei nomi, viene generato un avviso che indica che l'elemento ha un nome imprevisto. L'aggiunta di una @using direttiva per lo spazio dei nomi del componente rende disponibile il componente, che risolve l'avviso. Per altre informazioni, vedere la sezione Spazi dei nomi .

L'esempio Heading di componente illustrato in questa sezione non ha una @page direttiva, quindi il Heading componente non è direttamente accessibile a un utente tramite una richiesta diretta nel browser. Tuttavia, qualsiasi componente con una @page direttiva può essere annidato in un altro componente. Se il Heading componente è stato accessibile direttamente includendo @page "/heading" nella parte superiore del Razor file, il componente verrà eseguito il rendering per le richieste del browser in entrambi /heading e /heading-example.

Spazi dei nomi

In genere, lo spazio dei nomi di un componente deriva dallo spazio dei nomi radice dell'app e dalla posizione del componente (cartella) all'interno dell'app. Se lo spazio dei nomi radice dell'app è BlazorSample e il Counter componente si trova nella Pages cartella:

  • Lo Counter spazio dei nomi del componente è BlazorSample.Pages.
  • Il nome completo del tipo del componente è BlazorSample.Pages.Counter.

Per le cartelle personalizzate che contengono componenti, aggiungere una @using direttiva al componente padre o al file dell'app _Imports.razor . L'esempio seguente rende disponibili i componenti nella Components cartella:

@using BlazorSample.Components

Nota

@usingle direttive nel _Imports.razor file vengono applicate solo ai file (), non ai Razor file C# (.razor.cs).

È anche possibile fare riferimento ai componenti usando i nomi completi, che non richiedono una @using direttiva. L'esempio seguente fa riferimento direttamente al ProductDetail componente nella Components cartella dell'app:

<BlazorSample.Components.ProductDetail />

Lo spazio dei nomi di un componente creato con Razor è basato sul seguente (in ordine di priorità):

  • Direttiva @namespace nel Razor markup del file , ad esempio @namespace BlazorSample.CustomNamespace.
  • Il progetto è RootNamespace nel file di progetto , ad esempio <RootNamespace>BlazorSample</RootNamespace>.
  • Nome del progetto, tratto dal nome del file di progetto (.csproj) e dal percorso dalla radice del progetto al componente. Ad esempio, il framework viene risolto {PROJECT ROOT}/Pages/Index.razor con uno spazio dei nomi di progetto di BlazorSample (BlazorSample.csproj) nello spazio dei nomi BlazorSample.Pages per il Index componente. {PROJECT ROOT} è il percorso radice del progetto. I componenti seguono le regole di associazione dei nomi C#. Per il componente in questo esempio, i componenti nell'ambito Index sono tutti i componenti:
    • Nella stessa cartella , Pages.
    • I componenti nella radice del progetto che non specificano in modo esplicito uno spazio dei nomi diverso.

I seguenti non sono supportati:

  • Qualifica global:: .
  • Importazione di componenti con istruzioni aliaste using . Ad esempio, @using Foo = Bar non è supportato.
  • Nomi parzialmente qualificati. Ad esempio, non è possibile aggiungere @using BlazorSample a un componente e quindi fare riferimento al NavMenu componente nella cartella dell'app Shared (Shared/NavMenu.razor) con <Shared.NavMenu></Shared.NavMenu>.

Supporto della classe parziale

I componenti vengono generati come classi parziali C# e vengono creati usando uno degli approcci seguenti:

  • Un singolo file contiene codice C# definito in uno o più @code blocchi, markup HTML e Razor markup. Blazor i modelli di progetto definiscono i relativi componenti usando questo approccio a file singolo.
  • HTML e Razor markup vengono inseriti in un Razor file (.razor). Il codice C# viene inserito in un file code-behind definito come classe parziale (.cs).

Nota

Un foglio di stile del componente che definisce stili specifici del componente è un file separato (.css). BlazorL'isolamento CSS viene descritto più avanti in ASP.NET Core Blazor isolamento CSS.

Nell'esempio seguente viene illustrato il componente predefinito Counter con un @code blocco in un'app generata da un Blazor modello di progetto. Il markup e il codice C# si trovano nello stesso file. Questo è l'approccio più comune adottato nella creazione di componenti.

Pages/Counter.razor:

@page "/counter"

<h1>Counter</h1>

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

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

@code {
    private int currentCount = 0;

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

Il componente seguente Counter suddivide HTML e Razor markup dal codice C# usando un file code-behind con una classe parziale:

Pages/CounterPartialClass.razor:

@page "/counter-partial-class"

<h1>Counter</h1>

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

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

Pages/CounterPartialClass.razor.cs:

namespace BlazorSample.Pages
{
    public partial class CounterPartialClass
    {
        private int currentCount = 0;

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

@usingle direttive nel _Imports.razor file vengono applicate solo ai file (.razor), non ai Razor file C# (.cs). Aggiungere spazi dei nomi a un file di classe parziale in base alle esigenze.

Spazi dei nomi tipici usati dai componenti:

using System.Net.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.Web.Virtualization;
using Microsoft.JSInterop;

Gli spazi dei nomi tipici includono anche lo spazio dei nomi dell'app e lo spazio dei nomi corrispondente alla cartella dell'app Shared :

using BlazorSample;
using BlazorSample.Shared;

Specificare una classe base

La @inherits direttiva viene utilizzata per specificare una classe di base per un componente. Nell'esempio seguente viene illustrato come un componente può ereditare una classe base per fornire le proprietà e i metodi del componente. La BlazorRocksBase classe di base deriva da ComponentBase.

Pages/BlazorRocks.razor:

@page "/blazor-rocks"
@inherits BlazorRocksBase

<h1>@BlazorRocksText</h1>

BlazorRocksBase.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample
{
    public class BlazorRocksBase : ComponentBase
    {
        public string BlazorRocksText { get; set; } =
            "Blazor rocks the browser!";
    }
}

Parametri del componente

I parametri dei componenti passano i dati ai componenti e vengono definiti usando le proprietà C# pubbliche nella classe componente con l'attributo [Parameter]. Nell'esempio seguente, un tipo di riferimento predefinito (System.String) e un tipo riferimento definito dall'utente (PanelBody) vengono passati come parametri componente.

PanelBody.cs:

public class PanelBody
{
    public string Text { get; set; }
    public string Style { get; set; }
}

Shared/ParameterChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}

Avviso

La specifica dei valori iniziali per i parametri del componente è supportata, ma non crea un componente che scrive nei propri parametri dopo il rendering del componente per la prima volta. Per altre informazioni, vedere la sezione Parametri sovrascritti di questo articolo.

I Title parametri ParameterChild e Body del componente vengono impostati dagli argomenti nel tag HTML che esegue il rendering dell'istanza del componente. Il componente seguente ParameterParent esegue il rendering di due ParameterChild componenti:

  • Il rendering del primo ParameterChild componente viene eseguito senza fornire argomenti di parametro.
  • Il secondo ParameterChild componente riceve i valori per Title e Body dal ParameterParent componente, che usa un'espressione C# esplicita per impostare i valori delle PanelBodyproprietà dell'oggetto .

Pages/ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

Il markup HTML sottoposto a rendering seguente dal componente mostra ParameterChild i valori predefiniti del ParameterParent componente quando il ParameterParent componente non fornisce i valori dei parametri del componente. Quando il ParameterParent componente fornisce i valori dei parametri del componente, sostituisce i ParameterChild valori predefiniti del componente.

Nota

Per maggiore chiarezza, le classi di stile CSS sottoposte a rendering non vengono visualizzate nel markup HTML sottoposto a rendering seguente.

<h1>Child component (without attribute values)</h1>

<div>
    <div>Set By Child</div>
    <div>Set by child.</div>
</div>

<h1>Child component (with attribute values)</h1>

<div>
    <div>Set by Parent</div>
    <div>Set by parent.</div>
</div>

Assegnare un campo, una proprietà o un risultato C# a un metodo a un parametro del componente come valore di attributo HTML usando Razoril simbolo riservato @ di . Il componente seguente ParameterParent2 visualizza quattro istanze del componente precedente ParameterChild e imposta i relativi Title valori dei parametri su:

Il @ prefisso è obbligatorio per i parametri stringa. In caso contrario, il framework presuppone che sia impostato un valore letterale stringa.

Al di fuori dei parametri stringa, è consigliabile usare l'uso @ del prefisso per i nonliterali, anche quando non sono strettamente necessari.

Non è consigliabile usare il @ prefisso per i valori letterali (ad esempio, valori booleani), parole chiave (ad esempio, this) o null, ma è possibile scegliere di usarli se si vuole. Ad esempio, IsFixed="@true" è insolito ma supportato.

Le virgolette relative ai valori degli attributi dei parametri sono facoltative nella maggior parte dei casi in base alla specifica HTML5. Ad esempio, Value=this è supportato, anziché Value="this". Tuttavia, è consigliabile usare le virgolette perché è più facile ricordare e ampiamente adottato nelle tecnologie basate sul Web.

In tutta la documentazione, esempi di codice:

  • Usare sempre le virgolette. Esempio: Value="this".
  • I nonliterali usano sempre il @ prefisso, anche quando è facoltativo. Esempi: Title="@title", dove title è una variabile tipizzata da stringa. Count="@ct", dove ct è una variabile tipizzata dal numero.
  • I valori letterali, all'esterno delle Razor espressioni, evitano @sempre . Esempio: IsFixed="true".

Pages/ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

Nota

Quando si assegna un membro C# a un parametro del componente, anteporre al membro il @ simbolo e non anteporre mai l'attributo HTML del parametro.

Versione corretta:

<ParameterChild Title="@title" />

Non corretto:

<ParameterChild @Title="title" />

Diversamente dalle Razor pagine (.cshtml), Blazor non può eseguire operazioni asincrone in un'espressione Razor durante il rendering di un componente. Questo perché Blazor è progettato per il rendering di interfacce utente interattive. In un'interfaccia utente interattiva, lo schermo deve sempre visualizzare qualcosa, quindi non ha senso bloccare il flusso di rendering. Al contrario, il lavoro asincrono viene eseguito durante uno degli eventi del ciclo di vita asincroni. Dopo ogni evento del ciclo di vita asincrono, il componente può eseguire di nuovo il rendering. La sintassi seguente Razornon è supportata:

<ParameterChild Title="@await ..." />

Il codice nell'esempio precedente genera un errore del compilatore quando viene compilata l'app:

L'operatore 'await' può essere usato solo all'interno di un metodo asincrono. Valutare la possibilità di contrassegnare questo metodo con il modificatore "asincrono" e di modificarne il tipo restituito in "Task".

Per ottenere un valore per il Title parametro nell'esempio precedente in modo asincrono, il componente può usare l'eventoOnInitializedAsync del ciclo di vita, come illustrato nell'esempio seguente:

<ParameterChild Title="@title" />

@code {
    private string title;

    protected override async Task OnInitializedAsync()
    {
        title = await ...;
    }
}

Per altre informazioni, vedere ASP.NET Core ciclo di vita dei Razor componenti.

L'uso di un'espressione esplicita Razor per concatenare il testo con un risultato di espressione per l'assegnazione a un parametro non è supportato. L'esempio seguente cerca di concatenare il testo "Set by " con il valore della proprietà di un oggetto. Sebbene questa sintassi sia supportata in una Razor pagina (.cshtml), non è valida per l'assegnazione al parametro figlio Title in un componente. La sintassi seguente Razornon è supportata:

<ParameterChild Title="Set by @(panelData.Title)" />

Il codice nell'esempio precedente genera un errore del compilatore quando viene compilata l'app:

Gli attributi dei componenti non supportano contenuto complesso (C# misto e markup).

Per supportare l'assegnazione di un valore composto, usare un metodo, un campo o una proprietà. Nell'esempio seguente viene eseguita la concatenazione di "Set by " e il valore della proprietà di un oggetto nel metodo GetTitleC#:

Pages/ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

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

Avviso

La specifica dei valori iniziali per i parametri del componente è supportata, ma non crea un componente che scrive nei propri parametri dopo il rendering del componente per la prima volta. Per altre informazioni, vedere la sezione Parametri sovrascritti di questo articolo.

I parametri del componente devono essere dichiarati come proprietà automatiche, ovvero non devono contenere logica personalizzata nelle funzioni get di accesso o set . Ad esempio, la proprietà seguente StartData è una proprietà automatica:

[Parameter]
public DateTime StartData { get; set; }

Non inserire la logica personalizzata nella get funzione di accesso o set perché i parametri del componente sono esclusivamente destinati all'uso come canale per un componente padre per il flusso delle informazioni a un componente figlio. Se una set funzione di accesso di una proprietà del componente figlio contiene la logica che causa il rerendering del componente padre, viene restituito un ciclo di rendering infinito.

Per trasformare un valore di parametro ricevuto:

  • Lasciare la proprietà del parametro come proprietà automatica per rappresentare i dati non elaborati forniti.
  • Creare una proprietà o un metodo diverso per fornire i dati trasformati in base alla proprietà del parametro.

Eseguire l'override OnParametersSetAsync per trasformare un parametro ricevuto ogni volta che vengono ricevuti nuovi dati.

La scrittura di un valore iniziale in un parametro componente è supportata perché le assegnazioni di valori iniziali non interferiscono con il Blazorrendering automatico dei componenti. L'assegnazione seguente dell'oggetto locale DateTime corrente con DateTime.Now a StartData è una sintassi valida in un componente:

[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;

Dopo l'assegnazione iniziale di DateTime.Now, non assegnare un valore a StartData nel codice per sviluppatori. Per altre informazioni, vedere la sezione Parametri sovrascritti di questo articolo.

Parametri di route

I componenti possono specificare i parametri di route nel modello di route della @page direttiva . Il Blazor router usa i parametri di route per popolare i parametri dei componenti corrispondenti.

Sono supportati i parametri di route facoltativi. Nell'esempio seguente il text parametro facoltativo assegna il valore del segmento di route alla proprietà del Text componente. Se il segmento non è presente, il valore di Text viene impostato su "fantastic" nel metodo delOnInitialized ciclo di vita.

Pages/RouteParameter.razor:

@page "/route-parameter/{text?}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

Per informazioni sui parametri di route catch-all ({*pageRoute}), che acquisiscono i percorsi tra più limiti di cartella, vedere ASP.NET Core Blazor routing e navigazione.

Rendering dei frammenti di contenuto figlio

I componenti possono impostare il contenuto di un altro componente. Il componente di assegnazione fornisce il contenuto tra i tag di apertura e chiusura del componente figlio.

Nell'esempio seguente il RenderFragmentChild componente ha un ChildContent parametro componente che rappresenta un segmento dell'interfaccia utente di cui eseguire il rendering come .RenderFragment La posizione di ChildContent nel markup del componente è la posizione in Razor cui viene eseguito il rendering del contenuto nell'output HTML finale.

Shared/RenderFragmentChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

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

Importante

La proprietà che riceve il RenderFragment contenuto deve essere denominata ChildContent per convenzione.

I callback degli eventi non sono supportati per RenderFragment.

Il componente seguente RenderFragmentParent fornisce contenuto per il rendering RenderFragmentChild del contenuto inserendo il contenuto all'interno dei tag di apertura e chiusura del componente figlio.

Pages/RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

A causa del Blazor rendering del contenuto figlio, i componenti di rendering all'interno di un for ciclo richiedono una variabile di indice locale se la variabile ciclo di incremento viene usata nel RenderFragmentChild contenuto del componente. L'esempio seguente può essere aggiunto al componente precedente RenderFragmentParent :

<h1>Three children with an index variable</h1>

@for (int c = 0; c < 3; c++)
{
    var current = c;

    <RenderFragmentChild>
        Count: @current
    </RenderFragmentChild>
}

In alternativa, usare un foreach ciclo con Enumerable.Range anziché un for ciclo. L'esempio seguente può essere aggiunto al componente precedente RenderFragmentParent :

<h1>Second example of three children with an index variable</h1>

@foreach (var c in Enumerable.Range(0,3))
{
    <RenderFragmentChild>
        Count: @c
    </RenderFragmentChild>
}

I frammenti di rendering vengono usati per eseguire il rendering del contenuto figlio in tutte Blazor le app e vengono descritti con esempi nelle sezioni di articoli e articoli seguenti:

Nota

BlazorI componenti predefiniti Razor di framework usano la stessa ChildContent convenzione dei parametri del componente per impostare il contenuto. È possibile visualizzare i componenti che impostano il contenuto figlio cercando il nome ChildContent della proprietà del parametro del componente nella documentazione dell'API (filtra l'API con il termine di ricerca "ChildContent").

Eseguire il rendering di frammenti per la logica di rendering riutilizzabile

È possibile considerare i componenti figlio puramente come modo per riutilizzare la logica di rendering. Nel blocco di qualsiasi componente definire un RenderFragment e eseguire il rendering del @code frammento da qualsiasi posizione quante volte necessario:

<h1>Hello, world!</h1>

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo = __builder =>
    {
        <p>Welcome to your new app!</p>
    };
}

Per altre informazioni, vedere Riutilizzare la logica di rendering.

Parametri sovrascritti

Il framework impone in genere l'assegnazione Blazor di parametri padre-a-figlio sicuri:

  • I parametri non vengono sovrascritti in modo imprevisto.
  • Gli effetti collaterali sono ridotti al minimo. Ad esempio, vengono evitati altri rendering perché possono creare cicli di rendering infinito.

Un componente figlio riceve nuovi valori di parametro che potrebbero sovrascrivere i valori esistenti quando il componente padre esegue di nuovo il render. La sovrascrittura accidentale dei valori dei parametri in un componente figlio spesso si verifica quando si sviluppa il componente con uno o più parametri associati a dati e lo sviluppatore scrive direttamente in un parametro nel componente figlio:

  • Il rendering del componente figlio viene eseguito con uno o più valori di parametro dal componente padre.
  • Il figlio scrive direttamente sul valore di un parametro.
  • Il componente padre rerenders e sovrascrive il valore del parametro figlio.

Il potenziale per sovrascrivere i valori dei parametri si estende anche nelle funzioni di accesso delle proprietà set del componente figlio.

Importante

Le linee guida generali non consentono di creare componenti che scrivono direttamente nei propri parametri dopo il rendering del componente per la prima volta.

Prendere in considerazione il componente seguente Expander :

  • Esegue il rendering del contenuto figlio.
  • Attiva l'interruttore che mostra il contenuto figlio con un parametro del componente (Expanded).

Dopo che il componente seguente Expander illustra un parametro sovrascritto, viene visualizzato un componente modificato Expander per illustrare l'approccio corretto per questo scenario. Gli esempi seguenti possono essere inseriti in un'app di esempio locale per sperimentare i comportamenti descritti.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>

        @if (Expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    [Parameter]
    public bool Expanded { private get; set; }

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

    private void Toggle()
    {
        Expanded = !Expanded;
    }
}

Il Expander componente viene aggiunto al componente padre seguente ExpanderExample che può chiamare StateHasChanged:

Pages/ExpanderExample.razor:

@page "/expander-example"

<Expander Expanded="true">
    Expander 1 content
</Expander>

<Expander Expanded="true" />

<button @onclick="StateHasChanged">
    Call StateHasChanged
</button>

Inizialmente, i componenti si comportano in modo indipendente quando le Expander proprietà Expanded vengono attivate. I componenti figlio mantengono gli stati previsti.

Se StateHasChanged viene chiamato in un componente padre, il Blazor framework rerendere i componenti figlio se i parametri potrebbero essere stati modificati:

  • Per un gruppo di tipi di parametri che Blazor controllano in modo esplicito, Blazor rerendere un componente figlio se rileva che uno dei parametri è stato modificato.
  • Per i tipi di parametri deselezionati, Blazor esegue nuovamente il ripristino del componente figlio indipendentemente dal fatto che i parametri siano stati modificati. Il contenuto figlio rientra in questa categoria di tipi di parametri perché il contenuto figlio è di tipo RenderFragment, che è un delegato che fa riferimento ad altri oggetti modificabili.

Per il ExpanderExample componente:

  • Il primo Expander componente imposta il contenuto figlio in un oggetto potenzialmente modificabile RenderFragment, quindi una chiamata a StateHasChanged nel componente padre rerendere automaticamente il componente e potenzialmente sovrascrive il valore di Expanded al relativo valore intitiale di true.
  • Il secondo Expander componente non imposta il contenuto figlio. Pertanto, un oggetto potenzialmente mutable RenderFragment non esiste. Una chiamata a StateHasChanged nel componente padre non esegue automaticamente il renderendere il componente figlio, pertanto il valore del Expanded componente non viene sovrascritto.

Per mantenere lo stato nello scenario precedente, usare un campo privato nel Expander componente per mantenere lo stato disattivato.

Componente modificato Expander seguente:

  • Accetta il valore del Expanded parametro del componente dall'elemento padre.
  • Assegna il valore del parametro del componente a un campo privato (expanded) nell'eventoOnInitialized.
  • Usa il campo privato per mantenere lo stato di interruttore interno, che illustra come evitare di scrivere direttamente in un parametro.

Nota

Il consiglio in questa sezione si estende alla logica simile nelle funzioni di accesso ai parametri set del componente, che possono causare effetti collaterali indesiderati simili.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>

        @if (expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    private bool expanded;

    [Parameter]
    public bool Expanded { private get; set; }

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

    protected override void OnInitialized()
    {
        expanded = Expanded;
    }

    private void Toggle()
    {
        expanded = !expanded;
    }
}

Per altre informazioni, vedere Blazor Errore di associazione bidirezionale (dotnet/aspnetcore #24599).

Per altre informazioni sul rilevamento delle modifiche, informazioni sull'inserimento di informazioni sui tipi esatti che Blazor controllano, vedere rendering dei componenti ASP.NET CoreRazor.

Parametri splatting e arbitrari degli attributi

I componenti possono acquisire e eseguire il rendering di attributi aggiuntivi oltre ai parametri dichiarati del componente. Gli attributi aggiuntivi possono essere acquisiti in un dizionario e quindi splatted su un elemento quando il componente viene eseguito il rendering usando l'attributo @attributesRazor direttiva. Questo scenario è utile per definire un componente che produce un elemento di markup che supporta diverse personalizzazioni. Ad esempio, può essere noioso definire gli attributi separatamente per un <input> oggetto che supporta molti parametri.

Nel componente seguente Splat :

  • Il primo <input> elemento (id="useIndividualParams") usa i singoli parametri del componente.
  • Il secondo <input> elemento (id="useAttributesDict") usa lo splatting dell'attributo.

Pages/Splat.razor:

@page "/splat"

<input id="useIndividualParams"
       maxlength="@maxlength"
       placeholder="@placeholder"
       required="@required"
       size="@size" />

<input id="useAttributesDict"
       @attributes="InputAttributes" />

@code {
    private string maxlength = "10";
    private string placeholder = "Input placeholder text";
    private string required = "required";
    private string size = "50";

    private Dictionary<string, object> InputAttributes { get; set; } =
        new()
        {
            { "maxlength", "10" },
            { "placeholder", "Input placeholder text" },
            { "required", "required" },
            { "size", "50" }
        };
}

Gli elementi di cui è stato eseguito <input> il rendering nella pagina Web sono identici:

<input id="useIndividualParams"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

<input id="useAttributesDict"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

Per accettare attributi arbitrari, definire un parametro componente con la CaptureUnmatchedValues proprietà impostata su true:

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> InputAttributes { get; set; }
}

La CaptureUnmatchedValues proprietà su [Parameter] consente al parametro di corrispondere a tutti gli attributi che non corrispondono ad alcun altro parametro. Un componente può definire solo un singolo parametro con CaptureUnmatchedValues. Il tipo di proprietà usato con CaptureUnmatchedValues deve essere assegnabile da Dictionary<string, object> con chiavi di stringa. L'uso di IEnumerable<KeyValuePair<string, object>> o IReadOnlyDictionary<string, object> sono anche opzioni in questo scenario.

La posizione relativa alla posizione degli attributi dell'elemento @attributes è importante. Quando @attributes vengono splatted sull'elemento, gli attributi vengono elaborati da destra a sinistra (ultimo a primo). Si consideri l'esempio seguente di un componente padre che usa un componente figlio:

Shared/AttributeOrderChild1.razor:

<div @attributes="AdditionalAttributes" extra="5" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object> AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent1.razor:

@page "/attribute-order-parent-1"

<AttributeOrderChild1 extra="10" />

L'attributo AttributeOrderChild1 del extra componente è impostato a destra di @attributes. Il AttributeOrderParent1 rendering del componente contiene extra="5" quando viene passato attraverso l'attributo aggiuntivo <div> perché gli attributi vengono elaborati a destra a sinistra (ultimo al primo):

<div extra="5" />

Nell'esempio seguente, l'ordine <div>di extra e @attributes viene invertito nel componente figlio :

Shared/AttributeOrderChild2.razor:

<div extra="5" @attributes="AdditionalAttributes" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object> AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent2.razor:

@page "/attribute-order-parent-2"

<AttributeOrderChild2 extra="10" />

La <div> pagina Web di cui è stato eseguito il rendering del componente padre contiene extra="10" quando viene passato attraverso l'attributo aggiuntivo:

<div extra="10" />

Acquisire riferimenti ai componenti

I riferimenti ai componenti consentono di fare riferimento a un'istanza del componente per l'emissione di comandi. Per acquisire un riferimento al componente:

  • Aggiungere un @ref attributo al componente figlio.
  • Definire un campo con lo stesso tipo del componente figlio.

Quando viene eseguito il rendering del componente, il campo viene popolato con l'istanza del componente. È quindi possibile richiamare i metodi .NET nell'istanza.

Prendere in considerazione il componente seguente ReferenceChild che registra un messaggio quando ChildMethod viene chiamato.

Shared/ReferenceChild.razor:

@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> logger

@code {
    public void ChildMethod(int value)
    {
        logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}

Un riferimento al componente viene popolato solo dopo il rendering del componente e il relativo output include ReferenceChildl'elemento . Fino a quando non viene eseguito il rendering del componente, non c'è nulla da fare per fare riferimento.

Per modificare i riferimenti ai componenti al termine del rendering del componente, usare i OnAfterRender metodi oOnAfterRenderAsync.

Per usare una variabile di riferimento con un gestore eventi, usare un'espressione lambda o assegnare il delegato del gestore eventi nei OnAfterRender metodi oOnAfterRenderAsync. Ciò garantisce che la variabile di riferimento venga assegnata prima dell'assegnazione del gestore eventi.

L'approccio lambda seguente usa il componente precedente ReferenceChild .

Pages/ReferenceParent1.razor:

@page "/reference-parent-1"

<button @onclick="@(() => childComponent.ChildMethod(5))">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild childComponent;
}

L'approccio delegato seguente usa il componente precedente ReferenceChild .

Pages/ReferenceParent2.razor:

@page "/reference-parent-2"

<button @onclick="callChildMethod">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild childComponent;
    private Action callChildMethod;

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            callChildMethod = CallChildMethod;
        }
    }

    private void CallChildMethod()
    {
        childComponent.ChildMethod(5);
    }
}

Durante l'acquisizione dei riferimenti ai componenti, usare una sintassi simile per acquisire riferimenti agli elementi, l'acquisizione dei riferimenti ai componenti non è una funzionalità di interoperabilità JavaScript. I riferimenti ai componenti non vengono passati al codice JavaScript. I riferimenti ai componenti vengono usati solo nel codice .NET.

Importante

Non usare i riferimenti ai componenti per mutare lo stato dei componenti figlio. Usare invece i parametri dei componenti dichiarativi normali per passare i dati ai componenti figlio. L'uso dei parametri del componente comporta componenti figlio che rerendere automaticamente al momento corretto. Per altre informazioni, vedere la sezione parametri del componente e l'articolo ASP.NET Core Blazor data binding.

Contesto di sincronizzazione

Blazor usa un contesto di sincronizzazione (SynchronizationContext) per applicare un singolo thread logico di esecuzione. I metodi del ciclo di vita di un componente e i callback degli eventi generati da Blazor vengono eseguiti nel contesto di sincronizzazione.

Blazor ServerIl contesto di sincronizzazione tenta di emulare un ambiente a thread singolo in modo che corrisponda strettamente al modello WebAssembly nel browser, che è a thread singolo. In qualsiasi momento, il lavoro viene eseguito su un solo thread, che restituisce l'impressione di un singolo thread logico. Nessuna due operazioni vengono eseguite simultaneamente.

Evitare chiamate di blocco dei thread

In genere, non chiamare i metodi seguenti nei componenti. I metodi seguenti bloccano il thread di esecuzione e impediscono quindi all'app di riprendere il lavoro fino al completamento dell'oggetto sottostante Task :

Nota

Blazor gli esempi di documentazione che usano i metodi di blocco dei thread menzionati in questa sezione usano solo i metodi per scopi dimostrativi, non come indicazioni consigliate per la codifica. Ad esempio, alcune dimostrazioni di codice componente simulano un processo a esecuzione prolungata chiamando Thread.Sleep.

Richiamare i metodi componente esternamente per aggiornare lo stato

Se un componente deve essere aggiornato in base a un evento esterno, ad esempio un timer o un'altra notifica, usare il metodo, che invia l'esecuzione InvokeAsync del codice al Blazorcontesto di sincronizzazione. Si consideri, ad esempio, il servizio notificatore seguente che può inviare una notifica a qualsiasi componente in ascolto sullo stato aggiornato. Il Update metodo può essere chiamato da qualsiasi posizione nell'app.

TimerService.cs:

using System;
using System.Timers;
using Microsoft.Extensions.Logging;

public class TimerService : IDisposable
{
    private int elapsedCount;
    private readonly ILogger<TimerService> logger;
    private readonly NotifierService notifier;
    private Timer timer;

    public TimerService(NotifierService notifier, ILogger<TimerService> logger)
    {
        this.notifier = notifier;
        this.logger = logger;
    }

    public void Start()
    {
        if (timer is null)
        {
            timer = new();
            timer.AutoReset = true;
            timer.Interval = 10000;
            timer.Elapsed += HandleTimer;
            timer.Enabled = true;
            logger.LogInformation("Started");
        }
    }

    private async void HandleTimer(object source, ElapsedEventArgs e)
    {
        elapsedCount += 1;
        await notifier.Update("elapsedCount", elapsedCount);
        logger.LogInformation($"elapsedCount: {elapsedCount}");
    }

    public void Dispose()
    {
        timer?.Dispose();
    }
}

NotifierService.cs:

using System;
using System.Threading.Tasks;

public class NotifierService
{
    public async Task Update(string key, int value)
    {
        if (Notify != null)
        {
            await Notify.Invoke(key, value);
        }
    }

    public event Func<string, int, Task> Notify;
}

Registrare i servizi:

  • In un'app Blazor WebAssembly registrare i servizi come singleton in Program.cs:

    builder.Services.AddSingleton<NotifierService>();
    builder.Services.AddSingleton<TimerService>();
    
  • In un'app Blazor Server registrare i servizi come ambiti in Startup.ConfigureServices:

    services.AddScoped<NotifierService>();
    services.AddScoped<TimerService>();
    

Usare l'oggetto NotifierService per aggiornare un componente.

Pages/ReceiveNotifications.razor:

@page "/receive-notifications"
@implements IDisposable
@inject NotifierService Notifier
@inject TimerService Timer

<h1>Receive Notifications</h1>

<h2>Timer Service</h2>

<button @onclick="StartTimer">Start Timer</button>

<h2>Notifications</h2>

<p>
    Status:
    @if (lastNotification.key is not null)
    {
        <span>@lastNotification.key = @lastNotification.value</span>
    }
    else
    {
        <span>Awaiting first notification</span>
    }
</p>

@code {
    private (string key, int value) lastNotification;

    protected override void OnInitialized()
    {
        Notifier.Notify += OnNotify;
    }

    public async Task OnNotify(string key, int value)
    {
        await InvokeAsync(() =>
        {
            lastNotification = (key, value);
            StateHasChanged();
        });
    }

    private void StartTimer()
    {
        Timer.Start();
    }

    public void Dispose()
    {
        Notifier.Notify -= OnNotify;
    }
}

Nell'esempio precedente:

  • NotifierService richiama il metodo del OnNotify componente all'esterno del Blazorcontesto di sincronizzazione del componente. InvokeAsync viene usato per passare al contesto e alla coda corretti di un rendering. Per altre informazioni, vedere rendering dei componenti ASP.NET CoreRazor.
  • Il componente implementa IDisposable. Il OnNotify delegato viene annullato nel Dispose metodo, chiamato dal framework quando il componente viene eliminato. Per altre informazioni, vedere ciclo di vita del componente ASP.NET CoreRazor.

Usare @key per controllare la conservazione di elementi e componenti

Quando si esegue il rendering di un elenco di elementi o componenti e gli elementi o i componenti successivamente cambiano, Blazor è necessario decidere quali elementi o componenti precedenti possono essere conservati e come gli oggetti modello devono essere mappati a loro. In genere, questo processo è automatico e può essere ignorato, ma esistono casi in cui è possibile controllare il processo.

Prendere in considerazione i componenti e People i seguentiDetails:

  • Il Details componente riceve i dati (Data) dal componente padre People , che viene visualizzato in un <input> elemento. Qualsiasi elemento visualizzato specificato <input> può ricevere lo stato attivo della pagina dall'utente quando selezionano uno degli <input> elementi.
  • Il People componente crea un elenco di oggetti persona per la visualizzazione usando il Details componente. Ogni tre secondi, una nuova persona viene aggiunta alla raccolta.

Questa dimostrazione consente di:

  • Selezionare un <input> oggetto tra diversi componenti di cui è stato eseguito Details il rendering.
  • Studiare il comportamento dello stato attivo della pagina man mano che la raccolta persone aumenta automaticamente.

Shared/Details.razor:

<input value="@Data" />

@code {
    [Parameter]
    public string Data { get; set; }
}

Nel componente seguente People ogni iterazione dell'aggiunta di una persona OnTimerCallback comporta la Blazor ricompilazione dell'intera raccolta. Lo stato attivo della pagina rimane sulla stessa posizione di indice degli <input> elementi, quindi lo stato attivo viene spostato ogni volta che viene aggiunta una persona. Spostando lo stato attivo lontano dal comportamento desiderato dell'utente selezionato. Dopo aver dimostrato il comportamento negativo con il componente seguente, l'attributo @key della direttiva viene usato per migliorare l'esperienza dell'utente.

Pages/People.razor:

@page "/people"
@using System.Timers
@implements IDisposable

@foreach (var person in people)
{
    <Details Data="@person.Data" />
}

@code {
    private Timer timer = new Timer(3000);

    public List<Person> people =
        new()
        {
            { new Person { Data = "Person 1" } },
            { new Person { Data = "Person 2" } },
            { new Person { Data = "Person 3" } }
        };

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            people.Insert(0,
                new Person
                {
                    Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
                });
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();

    public class Person
    {
        public string Data { get; set; }
    }
}

Il contenuto della people raccolta viene modificato con voci inserite, eliminate o riordinate. Il rerendering può causare differenze di comportamento visibili. Ogni volta che una persona viene inserita nella raccolta, l'elemento precedente dell'elementopeople attualmente incentrato riceve lo stato attivo. Lo stato attivo dell'utente viene perso.

Il processo di mapping di elementi o componenti a una raccolta può essere controllato con l'attributo @key direttiva. L'uso di garantisce la conservazione di @key elementi o componenti in base al valore della chiave. Se il Details componente nell'esempio precedente viene chiaveto nell'elemento person , Blazor ignora i componenti di rerendering Details che non sono stati modificati.

Per modificare il People componente per usare l'attributo @key di direttiva con la people raccolta, aggiornare l'elemento <Details> al seguente:

<Details @key="person" Data="@person.Data" />

Quando la raccolta cambia, l'associazione people tra Details istanze e person istanze viene mantenuta. Quando un Person oggetto viene inserito all'inizio della raccolta, una nuova Details istanza viene inserita in tale posizione corrispondente. Altre istanze vengono lasciate invariate. Pertanto, lo stato attivo dell'utente non viene perso perché le persone vengono aggiunte alla raccolta.

Altri aggiornamenti della raccolta presentano lo stesso comportamento quando viene usato l'attributo @key direttiva:

  • Se un'istanza viene eliminata dalla raccolta, viene rimossa solo l'istanza del componente corrispondente dall'interfaccia utente. Altre istanze vengono lasciate invariate.
  • Se le voci della raccolta vengono riordinate, le istanze del componente corrispondenti vengono mantenute e riordinate nell'interfaccia utente.

Importante

Le chiavi sono locali per ogni elemento o componente del contenitore. Le chiavi non vengono confrontate a livello globale nel documento.

Quando usare @key

In genere, è consigliabile usare @key ogni volta che viene eseguito il rendering di un elenco (ad esempio, in un foreach blocco) e esiste un valore appropriato per definire l'oggetto @key.

È anche possibile usare @key per mantenere un sottoalbero elemento o componente quando un oggetto non cambia, come illustrato negli esempi seguenti.

Esempio 1:

<li @key="person">
    <input value="@person.Data" />
</li>

Esempio 2:

<div @key="person">
    @* other HTML elements *@
</div>

Se un'istanza person cambia, la direttiva dell'attributo @key forza Blazor :

  • Eliminare l'intero <li> o <div> e i loro discendenti.
  • Ricompilare il sottoalbero all'interno dell'interfaccia utente con nuovi elementi e componenti.

Ciò è utile per garantire che non venga mantenuto alcun stato dell'interfaccia utente quando la raccolta cambia all'interno di un sottoalbero.

Ambito di @key

La direttiva dell'attributo @key è inclusa nell'ambito dei propri fratelli all'interno del relativo padre.

Si consideri l'esempio seguente. Le first chiavi e second vengono confrontate tra loro nello stesso ambito dell'elemento esterno <div> :

<div>
    <div @key="first">...</div>
    <div @key="second">...</div>
</div>

L'esempio seguente illustra first e second le chiavi nei propri ambiti, non correlate tra loro e senza influenza tra loro. Ogni @key ambito si applica solo al relativo elemento padre, non tra gli elementi padre <div><div> :

<div>
    <div @key="first">...</div>
</div>
<div>
    <div @key="second">...</div>
</div>

Per il componente illustrato in precedenza, gli esempi seguenti eseguono il Details rendering person dei dati nello stesso @key ambito e illustrano i casi d'uso tipici per @key:

<div>
    @foreach (var person in people)
    {
        <Details @key="person" Data="@person.Data" />
    }
</div>
@foreach (var person in people)
{
    <div @key="person">
        <Details Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li @key="person">
            <Details Data="@person.Data" />
        </li>
    }
</ol>

Negli esempi seguenti viene eseguito solo l'ambito @key dell'elemento <div> o <li> che circonda ogni Details istanza del componente. Pertanto, person i dati per ogni membro della people raccolta non vengono chiaveti in ogni person istanza nei componenti di cui è stato eseguito Details il rendering. Evitare i modelli seguenti quando si usa @key:

@foreach (var person in people)
{
    <div>
        <Details @key="person" Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li>
            <Details @key="person" Data="@person.Data" />
        </li>
    }
</ol>

Quando non usare @key

È previsto un costo delle prestazioni durante il rendering con @key. Il costo delle prestazioni non è elevato, ma specificare solo se conservare @key l'elemento o il componente beneficia dell'app.

Anche se @key non viene usato, Blazor mantiene le istanze dell'elemento figlio e del componente il più possibile. L'unico vantaggio da usare @keyè controllare il mapping delle istanze del modello alle istanze dei componenti mantenute anziché Blazor selezionare il mapping.

Valori da usare per @key

In genere, è consigliabile specificare uno dei valori seguenti per @key:

  • Istanze dell'oggetto modello. Ad esempio, l'istanza (person) è stata usata nell'esempio Person precedente. Ciò garantisce la conservazione in base all'uguaglianza dei riferimenti a oggetti.
  • Identificatori univoci. Ad esempio, gli identificatori univoci possono essere basati sui valori di chiave primaria di tipo int, stringo Guid.

Assicurarsi che i valori usati per @key non si scontrino. Se i valori di confronto vengono rilevati all'interno dello stesso elemento padre, Blazor genera un'eccezione perché non può eseguire il mapping deterministico di elementi o componenti precedenti a nuovi elementi o componenti. Usare solo valori distinti, ad esempio istanze di oggetti o valori chiave primaria.

Applicare un attributo

Gli attributi possono essere applicati ai componenti con la @attribute direttiva. Nell'esempio seguente viene applicato l'attributo[Authorize] alla classe del componente:

@page "/"
@attribute [Authorize]

Attributi degli elementi HTML condizionali

Le proprietà dell'attributo dell'elemento HTML vengono impostate in modo condizionale in base al valore .NET. Se il valore è false o null, la proprietà non è impostata. Se il valore è true, la proprietà è impostata.

Nell'esempio seguente determina IsCompleted se la <input> proprietà dell'elemento checked è impostata.

Pages/ConditionalAttribute.razor:

@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}

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

Avviso

Alcuni attributi HTML, ad esempio aria-pressed, non funzionano correttamente quando il tipo .NET è un boologgetto . In questi casi, usare un string tipo anziché un boologgetto .

HTML non elaborato

Le stringhe vengono normalmente sottoposte a rendering usando nodi di testo DOM, il che significa che qualsiasi markup che possono contenere viene ignorato e considerato come testo letterale. Per eseguire il rendering di HTML non elaborato, eseguire il wrapping del contenuto HTML in un MarkupString valore. Il valore viene analizzato come HTML o SVG e inserito nel DOM.

Avviso

Il rendering di HTML non elaborato costruito da qualsiasi origine non attendibile è un rischio di sicurezza ed è sempre necessario evitare.

Nell'esempio seguente viene illustrato l'uso del tipo per aggiungere un blocco di contenuto HTML statico all'output MarkupString sottoposto a rendering di un componente.

Pages/MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

Razor Modelli

È possibile definire frammenti di rendering usando Razor la sintassi del modello per definire un frammento di interfaccia utente. Razor i modelli usano il formato seguente:

@<{HTML tag}>...</{HTML tag}>

Nell'esempio seguente viene illustrato come specificare e visualizzare RenderFragment i RenderFragment<TValue> modelli direttamente in un componente. I frammenti di rendering possono essere passati anche come argomenti ai componenti modelli.

Pages/RazorTemplate.razor:

@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}

Output sottoposto a rendering del codice precedente:

<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>

Asset statici

Blazorsegue la convenzione delle app ASP.NET Core per gli asset statici. Gli asset statici si trovano nella cartella () delweb root progetto owwwroot delle cartelle nella wwwroot cartella.

Usare un percorso relativo alla base (/) per fare riferimento alla radice Web per un asset statico. Nell'esempio seguente si logo.png trova fisicamente nella {PROJECT ROOT}/wwwroot/images cartella. {PROJECT ROOT} è la radice del progetto dell'app.

<img alt="Company logo" src="/images/logo.png" />

I componenti non supportano la notazione della barra tilde (~/).

Per informazioni sull'impostazione del percorso di base di un'app, vedere Host e distribuzione ASP.NET Core Blazor.

I helper tag non sono supportati nei componenti

Tag Helpers non sono supportati nei componenti. Per fornire funzionalità simili a Tag helper in Blazor, creare un componente con la stessa funzionalità dell'helper tag e usare invece il componente.

Immagini vettoriali scalabili (SVG)

Poiché Blazor esegue il rendering di immagini HTML, supportate dal browser, incluse le immagini SVG (Scalable Vector Graphics) (.svg), sono supportate tramite il <img> tag:

<img alt="Example image" src="image.svg" />

Analogamente, le immagini SVG sono supportate nelle regole CSS di un file del foglio di stile (.css):

.element-class {
    background-image: url("image.svg");
}

Comportamento di rendering degli spazi vuoti

A meno che la @preservewhitespace direttiva non venga usata con un valore di true, spazi vuoti aggiuntivi viene rimosso per impostazione predefinita se:

  • Iniziale o finale all'interno di un elemento.
  • Iniziale o finale all'interno di un RenderFragment/RenderFragment<TValue> parametro (ad esempio, contenuto figlio passato a un altro componente).
  • Precede o segue un blocco di codice C#, ad esempio @if o @foreach.

La rimozione dello spazio vuoto potrebbe influire sull'output sottoposto a rendering quando si usa una regola CSS, ad esempio white-space: pre. Per disabilitare questa ottimizzazione delle prestazioni e mantenere lo spazio vuoto, eseguire una delle azioni seguenti:

  • Aggiungere la @preservewhitespace true direttiva nella parte superiore del Razor file (.razor) per applicare la preferenza a un componente specifico.
  • Aggiungere la @preservewhitespace true direttiva all'interno di un file per applicare la preferenza a una _Imports.razor sottodirectory o all'intero progetto.

Nella maggior parte dei casi, non è necessaria alcuna azione, poiché le app continuano in genere a comportarsi normalmente (ma più velocemente). Se la rimozione dello spazio vuoto causa un problema di rendering per un determinato componente, usare @preservewhitespace true in tale componente per disabilitare questa ottimizzazione.

Supporto dei parametri di tipo generico

La @typeparam direttiva dichiara un parametro di tipo generico per la classe componente generata:

@typeparam TItem

Nell'esempio seguente il ListGenericTypeItems1 componente viene genericamente digitato come TExample.

Shared/ListGenericTypeItems1.razor:

@typeparam TExample

@if (ExampleList is not null)
{
    <ul>
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public IEnumerable<TExample> ExampleList{ get; set; }
}

Il componente seguente GenericTypeExample1 esegue il rendering di due ListGenericTypeItems1 componenti:

  • I dati stringa o integer vengono assegnati al ExampleList parametro di ogni componente.
  • Tipo string o int corrispondente al tipo dei dati assegnati viene impostato per il parametro di tipo (TExample) di ogni componente.

Pages/GenericTypeExample1.razor:

@page "/generic-type-example-1"

<h1>Generic Type Example 1</h1>

<ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" 
                       TExample="string" />

<ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })" 
                       TExample="int" />

Per altre informazioni, vedere gli articoli seguenti:

Blazor le app vengono compilate usando Razor componenti, noti in modo informale come Blazor componenti. Un componente è una parte autonoma dell'interfaccia utente con la logica di elaborazione per abilitare il comportamento dinamico. I componenti possono essere annidati, riutilizzati, condivisi tra progetti e usati nelle app MVC e Razor Pages.

Classi componente

I componenti vengono implementati usando una combinazione di markup C# e HTML nei Razor file di componente con l'estensione del .razor file.

Sintassi Razor

I componenti usano Razor la sintassi. Due Razor funzionalità sono ampiamente usate da componenti, direttive e attributi di direttiva. Queste sono parole chiave riservate con prefisso @ che vengono visualizzate nel Razor markup:

  • Direttive: modificare il modo in cui il markup del componente viene analizzato o funzioni. Ad esempio, la @page direttiva specifica un componente instradabile con un modello di route e può essere raggiunto direttamente dalla richiesta di un utente nel browser in un URL specifico.
  • Attributi di direttiva: modificare il modo in cui un elemento componente viene analizzato o funzioni. Ad esempio, l'attributo @bind della direttiva per un <input> elemento associa i dati al valore dell'elemento.

Gli attributi delle direttive e delle direttive usati nei componenti sono illustrati ulteriormente in questo articolo e altri articoli del Blazor set di documentazione. Per informazioni generali sulla sintassi, vedere Razor informazioni di riferimento sullaRazor sintassi per ASP.NET Core.

Nomi

Il nome di un componente deve iniziare con un carattere maiuscolo:

  • ProductDetail.razor è valido.
  • productDetail.razor non è valido.

Le convenzioni di denominazione comuni Blazor usate in tutta la Blazor documentazione includono:

  • I percorsi dei file del componente usano case Pascal† e vengono visualizzati prima di visualizzare esempi di codice componente. I percorsi indicano percorsi di cartelle tipici. Ad esempio, Pages/ProductDetail.razor indica che il ProductDetail componente ha un nome file di ProductDetail.razor e risiede nella Pages cartella dell'app.
  • I percorsi dei file dei componenti per i componenti instradabili corrispondono agli URL con trattini visualizzati per gli spazi tra le parole nel modello di route di un componente. Ad esempio, un componente con un ProductDetail modello di route di /product-detail (@page "/product-detail") viene richiesto in un browser all'URL /product-detailrelativo .

†Pascal case (maiuscolo) è una convenzione di denominazione senza spazi e punteggiatura e con la prima lettera di ogni parola maiuscola, inclusa la prima parola.

Routing

Il routing in Blazor viene ottenuto fornendo un modello di route a ogni componente accessibile nell'app con una @page direttiva. Quando viene compilato un Razor file con una direttiva, la classe generata viene assegnata a una @pageRouteAttribute specifica del modello di route. In fase di esecuzione, il router cerca classi di componenti con un RouteAttribute componente e esegue il rendering di un modello di route corrispondente all'URL richiesto.

Il componente seguente HelloWorld usa un modello di route di /hello-world. La pagina Web di cui è stato eseguito il rendering per il componente viene raggiunta all'URL /hello-worldrelativo. Quando si esegue un'app Blazor in locale con il protocollo, l'host e la porta predefiniti, il HelloWorld componente viene richiesto nel browser in https://localhost:5001/hello-world. I componenti che producono pagine Web si trovano in genere nella cartella, ma è possibile usare qualsiasi cartella per contenere componenti, inclusi all'interno Pages di cartelle annidate.

Pages/HelloWorld.razor:

@page "/hello-world"

<h1>Hello World!</h1>

Il componente precedente viene caricato nel browser /hello-world indipendentemente dal fatto che si aggiunge o meno il componente allo spostamento dell'interfaccia utente dell'app. Facoltativamente, i componenti possono essere aggiunti al NavMenu componente in modo che un collegamento al componente venga visualizzato nello spostamento basato sull'interfaccia utente dell'app.

Per il componente precedente HelloWorld , è possibile aggiungere un NavLink componente al NavMenu componente nella Shared cartella. Per altre informazioni, incluse le descrizioni dei NavLink componenti e, vedere ASP.NET Core Blazor routing eNavMenu spostamento.

markup

L'interfaccia utente di un componente viene definita usando Razor la sintassi, costituita da Razor markup, C#e HTML. Quando viene compilata un'app, la logica di rendering HTML e C# viene convertita in una classe componente. Il nome della classe generata corrisponde al nome del file.

I membri della classe componente sono definiti in uno o più @code blocchi. In @code blocchi, lo stato del componente viene specificato ed elaborato con C#:

  • Inizializzatori di proprietà e di campo.
  • Valori dei parametri degli argomenti passati dai componenti padre e parametri di route.
  • Metodi per la gestione degli eventi utente, gli eventi del ciclo di vita e la logica del componente personalizzata.

I membri del componente vengono usati nella logica di rendering usando espressioni C# che iniziano con il @ simbolo. Ad esempio, viene eseguito il rendering di un campo C# anteponendo @ al nome del campo. Il componente seguente Markup valuta ed esegue il rendering:

  • headingFontStyle per il valore font-style della proprietà CSS dell'elemento titolo.
  • headingText per il contenuto dell'elemento titolo.

Pages/Markup.razor:

@page "/markup"

<h1 style="font-style:@headingFontStyle">@headingText</h1>

@code {
    private string headingFontStyle = "italic";
    private string headingText = "Put on your new Blazor!";
}

Nota

Esempi in tutta la Blazor documentazione specificano il private modificatore di accesso per i membri privati. I membri privati hanno come ambito la classe di un componente. Tuttavia, C# presuppone che il private modificatore di accesso non sia presente quando non è presente alcun modificatore di accesso, quindi contrassegnare in modo esplicito i membri "private" nel proprio codice è facoltativo. Per altre informazioni sui modificatori di accesso, vedere Access Modifiers (Guida per programmatori C#).

Il Blazor framework elabora internamente un componente come albero di rendering, ovvero la combinazione del modello DOM (Document Object Model) di un componente e del modello CSSOM (Cascading Style Sheet Object Model). Dopo il rendering iniziale del componente, l'albero di rendering del componente viene rigenerato in risposta agli eventi. Blazor confronta la nuova struttura ad albero di rendering con l'albero di rendering precedente e applica eventuali modifiche al DOM del browser per la visualizzazione. Per altre informazioni, vedere rendering dei componenti ASP.NET CoreRazor.

I componenti sono classi C# comuni e possono essere posizionati in qualsiasi punto all'interno di un progetto. I componenti che producono pagine Web si trovano in genere nella Pages cartella . I componenti non di pagina vengono spesso inseriti nella Shared cartella o in una cartella personalizzata aggiunta al progetto.

Razor la sintassi per le strutture di controllo, le direttive e gli attributi della direttiva C# sono minuscoli (esempi: @if, @code, @bind). I nomi delle proprietà sono maiuscoli ,ad esempio @Body per LayoutComponentBase.Body.

I metodi asincroni (async) non supportano la restituzione void

Il Blazor framework non tiene traccia voiddei metodi asincroni (async). Di conseguenza, le eccezioni non vengono rilevate se void viene restituito. Restituisce sempre un Task oggetto da metodi asincroni.

Componenti annidati

I componenti possono includere altri componenti dichiarandoli usando la sintassi HTML. Il markup per l'uso di un componente è simile a un tag HTML, in cui il nome del tag è il tipo di componente.

Si consideri il componente seguente Heading , che può essere usato da altri componenti per visualizzare un'intestazione.

Shared/Heading.razor:

<h1 style="font-style:@headingFontStyle">Heading Example</h1>

@code {
    private string headingFontStyle = "italic";
}

Il markup seguente nel componente esegue il rendering del HeadingExample componente precedente Heading nella posizione in cui viene visualizzato il <Heading /> tag.

Pages/HeadingExample.razor:

@page "/heading-example"

<Heading />

Se un componente contiene un elemento HTML con una prima lettera maiuscola che non corrisponde a un nome di componente all'interno dello stesso spazio dei nomi, viene generato un avviso che indica che l'elemento ha un nome imprevisto. L'aggiunta di una @using direttiva per lo spazio dei nomi del componente rende disponibile il componente, che risolve l'avviso. Per altre informazioni, vedere la sezione Spazi dei nomi .

L'esempio Heading di componente illustrato in questa sezione non ha una @page direttiva, quindi il Heading componente non è direttamente accessibile a un utente tramite una richiesta diretta nel browser. Tuttavia, qualsiasi componente con una @page direttiva può essere annidato in un altro componente. Se il Heading componente è stato accessibile direttamente includendo @page "/heading" nella parte superiore del Razor file, il rendering del componente verrà eseguito per le richieste del browser sia /heading in che /heading-examplein .

Spazi dei nomi

In genere, lo spazio dei nomi di un componente è derivato dallo spazio dei nomi radice dell'app e dal percorso (cartella) del componente all'interno dell'app. Se lo spazio dei nomi radice dell'app è BlazorSample e il Counter componente si trova nella Pages cartella :

  • Lo Counter spazio dei nomi del componente è BlazorSample.Pages.
  • Il nome completo del tipo del componente è BlazorSample.Pages.Counter.

Per le cartelle personalizzate che contengono componenti, aggiungere una @using direttiva al componente padre o al file dell'app _Imports.razor . L'esempio seguente rende disponibili i componenti nella Components cartella :

@using BlazorSample.Components

Nota

@usingle direttive nel _Imports.razor file vengono applicate solo ai file (.razor), non ai Razor file C# (.cs).

È anche possibile fare riferimento ai componenti usando i nomi completi, che non richiedono una @using direttiva. L'esempio seguente fa riferimento direttamente al ProductDetail componente nella Components cartella dell'app:

<BlazorSample.Components.ProductDetail />

Lo spazio dei nomi di un componente creato con Razor è basato sul seguente (in ordine di priorità):

  • Direttiva @namespace nel Razor markup del file , ad esempio @namespace BlazorSample.CustomNamespace.
  • Il progetto si trova nel file di RootNamespace progetto , ad esempio <RootNamespace>BlazorSample</RootNamespace>.
  • Nome del progetto, ricavato dal nome file del file di progetto (.csproj) e dal percorso dalla radice del progetto al componente. Ad esempio, il framework viene {PROJECT ROOT}/Pages/Index.razor risolto con uno spazio dei nomi del progetto di BlazorSample (BlazorSample.csproj) nello spazio dei nomi BlazorSample.Pages per il Index componente. {PROJECT ROOT} è il percorso radice del progetto. I componenti seguono le regole di associazione dei nomi C#. Per il Index componente in questo esempio, i componenti nell'ambito sono tutti i componenti:
    • Nella stessa cartella , Pages.
    • I componenti nella radice del progetto che non specificano in modo esplicito uno spazio dei nomi diverso.

Le opzioni seguenti non sono supportate:

  • Qualifica global:: .
  • Importazione di componenti con istruzioni con using alias. Ad esempio, @using Foo = Bar non è supportato.
  • Nomi parzialmente qualificati. Ad esempio, non è possibile aggiungere @using BlazorSample a un componente e quindi fare riferimento al NavMenu componente nella cartella dell'app Shared (Shared/NavMenu.razor) con <Shared.NavMenu></Shared.NavMenu>.

Supporto di classi parziali

I componenti vengono generati come classi parziali C# e vengono creati usando uno degli approcci seguenti:

  • Un singolo file contiene codice C# definito in uno o più @code blocchi, markup HTML e Razor markup. Blazor I modelli di progetto definiscono i relativi componenti usando questo approccio a file singolo.
  • HTML e Razor markup vengono inseriti in un Razor file (.razor). Il codice C# viene inserito in un file code-behind definito come classe parziale (.cs).

L'esempio seguente mostra il componente predefinito Counter con un @code blocco in un'app generata da un Blazor modello di progetto. Il markup e il codice C# si trovano nello stesso file. Questo è l'approccio più comune adottato nella creazione di componenti.

Pages/Counter.razor:

@page "/counter"

<h1>Counter</h1>

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

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

@code {
    private int currentCount = 0;

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

Il componente seguente Counter suddivide HTML e Razor markup dal codice C# usando un file code-behind con una classe parziale:

Pages/CounterPartialClass.razor:

@page "/counter-partial-class"

<h1>Counter</h1>

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

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

Pages/CounterPartialClass.razor.cs:

namespace BlazorSample.Pages
{
    public partial class CounterPartialClass
    {
        private int currentCount = 0;

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

@usingle direttive nel _Imports.razor file vengono applicate solo ai file (.razor), non ai Razor file C# (.cs). Aggiungere spazi dei nomi a un file di classe parziale in base alle esigenze.

Spazi dei nomi tipici usati dai componenti:

using System.Net.Http;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;

Gli spazi dei nomi tipici includono anche lo spazio dei nomi dell'app e lo spazio dei nomi corrispondente alla cartella dell'app Shared :

using BlazorSample;
using BlazorSample.Shared;

Specificare una classe base

La @inherits direttiva viene utilizzata per specificare una classe di base per un componente. Nell'esempio seguente viene illustrato come un componente può ereditare una classe base per fornire le proprietà e i metodi del componente. La BlazorRocksBase classe di base deriva da ComponentBase.

Pages/BlazorRocks.razor:

@page "/blazor-rocks"
@inherits BlazorRocksBase

<h1>@BlazorRocksText</h1>

BlazorRocksBase.cs:

using Microsoft.AspNetCore.Components;

namespace BlazorSample
{
    public class BlazorRocksBase : ComponentBase
    {
        public string BlazorRocksText { get; set; } =
            "Blazor rocks the browser!";
    }
}

Parametri del componente

I parametri dei componenti passano i dati ai componenti e vengono definiti usando le proprietà C# pubbliche nella classe componente con l'attributo [Parameter]. Nell'esempio seguente, un tipo di riferimento predefinito (System.String) e un tipo riferimento definito dall'utente (PanelBody) vengono passati come parametri componente.

PanelBody.cs:

public class PanelBody
{
    public string Text { get; set; }
    public string Style { get; set; }
}

Shared/ParameterChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">@Title</div>
    <div class="card-body" style="font-style:@Body.Style">
        @Body.Text
    </div>
</div>

@code {
    [Parameter]
    public string Title { get; set; } = "Set By Child";

    [Parameter]
    public PanelBody Body { get; set; } =
        new PanelBody()
        {
            Text = "Set by child.",
            Style = "normal"
        };
}

Avviso

La specifica dei valori iniziali per i parametri del componente è supportata, ma non crea un componente che scrive nei propri parametri dopo il rendering del componente per la prima volta. Per altre informazioni, vedere la sezione Parametri sovrascritti di questo articolo.

I Title parametri ParameterChild e Body del componente vengono impostati dagli argomenti nel tag HTML che esegue il rendering dell'istanza del componente. Il componente seguente ParameterParent esegue il rendering di due ParameterChild componenti:

  • Il rendering del primo ParameterChild componente viene eseguito senza fornire argomenti di parametro.
  • Il secondo ParameterChild componente riceve i valori per Title e Body dal ParameterParent componente, che usa un'espressione C# esplicita per impostare i valori delle PanelBodyproprietà dell'oggetto .

Pages/ParameterParent.razor:

@page "/parameter-parent"

<h1>Child component (without attribute values)</h1>

<ParameterChild />

<h1>Child component (with attribute values)</h1>

<ParameterChild Title="Set by Parent"
                Body="@(new PanelBody() { Text = "Set by parent.", Style = "italic" })" />

Il markup HTML sottoposto a rendering seguente dal componente mostra ParameterChild i valori predefiniti del ParameterParent componente quando il ParameterParent componente non fornisce i valori dei parametri del componente. Quando il ParameterParent componente fornisce i valori dei parametri del componente, sostituisce i ParameterChild valori predefiniti del componente.

Nota

Per maggiore chiarezza, le classi di stile CSS sottoposte a rendering non vengono visualizzate nel markup HTML sottoposto a rendering seguente.

<h1>Child component (without attribute values)</h1>

<div>
    <div>Set By Child</div>
    <div>Set by child.</div>
</div>

<h1>Child component (with attribute values)</h1>

<div>
    <div>Set by Parent</div>
    <div>Set by parent.</div>
</div>

Assegnare un campo, una proprietà o un risultato C# a un metodo a un parametro del componente come valore di attributo HTML usando Razoril simbolo riservato @ di . Il componente seguente ParameterParent2 visualizza quattro istanze del componente precedente ParameterChild e imposta i relativi Title valori dei parametri su:

Il @ prefisso è necessario per i parametri stringa. In caso contrario, il framework presuppone che sia impostato un valore letterale stringa.

Al di fuori dei parametri stringa, è consigliabile usare l'uso @ del prefisso per nonliterali, anche quando non sono strettamente necessari.

Non è consigliabile usare il @ prefisso per i valori letterali (ad esempio, valori booleani), le parole chiave (ad esempio, this) o , ma nullè possibile scegliere di usarle se si desidera. Ad esempio, è insolito, IsFixed="@true" ma supportato.

Le virgolette relative ai valori degli attributi dei parametri sono facoltative nella maggior parte dei casi per la specifica HTML5. Ad esempio, Value=this è supportato anziché Value="this". È tuttavia consigliabile usare virgolette perché è più facile ricordare e ampiamente adottato in tecnologie basate sul Web.

In tutta la documentazione, esempi di codice:

  • Usare sempre virgolette. Esempio: Value="this".
  • I nonliterali usano sempre il @ prefisso, anche quando è facoltativo. Esempi: Title="@title", dove title è una variabile tipizzata di stringa. Count="@ct", dove ct è una variabile numerata.
  • Valori letterali, all'esterno delle Razor espressioni, evitare @sempre . Esempio: IsFixed="true".

Pages/ParameterParent2.razor:

@page "/parameter-parent-2"

<ParameterChild Title="@title" />

<ParameterChild Title="@GetTitle()" />

<ParameterChild Title="@DateTime.Now.ToLongDateString()" />

<ParameterChild Title="@panelData.Title" />

@code {
    private string title = "From Parent field";
    private PanelData panelData = new PanelData();

    private string GetTitle()
    {
        return "From Parent method";
    }

    private class PanelData
    {
        public string Title { get; set; } = "From Parent object";
    }
}

Nota

Quando si assegna un membro C# a un parametro del componente, assegnare il prefisso al membro con il @ simbolo e non digitare mai il prefisso dell'attributo HTML del parametro.

Versione corretta:

<ParameterChild Title="@title" />

Non corretto:

<ParameterChild @Title="title" />

A differenza delle Razor pagine (.cshtml), Blazor non è possibile eseguire operazioni asincrone in un'espressione durante il rendering di un Razor componente. Questo perché Blazor è progettato per il rendering di interfacce utente interattive. In un'interfaccia utente interattiva, lo schermo deve sempre visualizzare qualcosa, quindi non ha senso bloccare il flusso di rendering. Il lavoro asincrono viene invece eseguito durante uno degli eventi del ciclo di vita asincroni. Dopo ogni evento del ciclo di vita asincrono, il componente può nuovamente eseguire il rendering. La sintassi seguente Razornon è supportata:

<ParameterChild Title="@await ..." />

Il codice nell'esempio precedente genera un errore del compilatore quando l'app viene compilata:

L'operatore 'await' può essere usato solo all'interno di un metodo asincrono. Prendere in considerazione la contrassegnatura di questo metodo con il modificatore "asincrono" e la modifica del tipo restituito in 'Task'.

Per ottenere un valore per il Title parametro nell'esempio precedente in modo asincrono, il componente può usare l'evento del ciclo di vita, come illustrato nell'esempioOnInitializedAsync seguente:

<ParameterChild Title="@title" />

@code {
    private string title;

    protected override async Task OnInitializedAsync()
    {
        title = await ...;
    }
}

Per altre informazioni, vedere ciclo di vita del componente ASP.NET CoreRazor.

L'uso di un'espressione esplicita Razor per concatenare il testo con un risultato dell'espressione per l'assegnazione a un parametro non è supportato. L'esempio seguente cerca di concatenare il testo "Set by " con il valore della proprietà di un oggetto. Anche se questa sintassi è supportata in una Razor pagina (.cshtml), non è valida per l'assegnazione al parametro del Title figlio in un componente. La sintassi seguente Razornon è supportata:

<ParameterChild Title="Set by @(panelData.Title)" />

Il codice nell'esempio precedente genera un errore del compilatore quando l'app viene compilata:

Gli attributi dei componenti non supportano contenuto complesso (C# misto e markup).

Per supportare l'assegnazione di un valore composto, usare un metodo, un campo o una proprietà. Nell'esempio seguente viene eseguita la concatenazione di "Set by " e il valore della proprietà di un oggetto nel metodo GetTitleC# :

Pages/ParameterParent3.razor:

@page "/parameter-parent-3"

<ParameterChild Title="@GetTitle()" />

@code {
    private PanelData panelData = new PanelData();

    private string GetTitle() => $"Set by {panelData.Title}";

    private class PanelData
    {
        public string Title { get; set; } = "Parent";
    }
}

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

Avviso

La fornitura di valori iniziali per i parametri del componente è supportata, ma non crea un componente che scrive nei propri parametri dopo il rendering del componente per la prima volta. Per altre informazioni, vedere la sezione Parametri sovrascritti di questo articolo.

I parametri dei componenti devono essere dichiarati come proprietà automatiche, ovvero che non devono contenere logica personalizzata nelle funzioni get di accesso o set . Ad esempio, la proprietà seguente StartData è una proprietà automatica:

[Parameter]
public DateTime StartData { get; set; }

Non inserire la logica personalizzata nella funzione di accesso o set perché i parametri del componente sono puramente destinati all'uso get come canale per un componente padre per il flusso delle informazioni in un componente figlio. Se una set funzione di accesso di una proprietà del componente figlio contiene la logica che causa il rerendering del componente padre, i risultati di un ciclo di rendering infinito.

Per trasformare un valore di parametro ricevuto:

  • Lasciare la proprietà del parametro come proprietà automatica per rappresentare i dati non elaborati forniti.
  • Creare una proprietà o un metodo diverso per fornire i dati trasformati in base alla proprietà del parametro.

Eseguire l'override OnParametersSetAsync per trasformare un parametro ricevuto ogni volta che vengono ricevuti nuovi dati.

La scrittura di un valore iniziale in un parametro componente è supportata perché le assegnazioni di valori iniziali non interferiscono con il Blazorrendering automatico dei componenti. L'assegnazione seguente dell'oggetto locale DateTime corrente con DateTime.Now a StartData è una sintassi valida in un componente:

[Parameter]
public DateTime StartData { get; set; } = DateTime.Now;

Dopo l'assegnazione iniziale di DateTime.Now, non assegnare un valore al StartData codice per sviluppatori. Per altre informazioni, vedere la sezione Parametri sovrascritti di questo articolo.

Parametri di route

I componenti possono specificare parametri di route nel modello di route della @page direttiva. Il Blazor router usa parametri di route per popolare i parametri dei componenti corrispondenti.

Pages/RouteParameter.razor:

@page "/route-parameter"
@page "/route-parameter/{text}"

<h1>Blazor is @Text!</h1>

@code {
    [Parameter]
    public string Text { get; set; }

    protected override void OnInitialized()
    {
        Text = Text ?? "fantastic";
    }
}

I parametri di route facoltativi non sono supportati, quindi due @page direttive vengono applicate nell'esempio precedente. La prima @page direttiva consente la navigazione al componente senza un parametro di route. La seconda @page direttiva riceve il {text} parametro di route e assegna il valore alla Text proprietà.

Per informazioni sui parametri di route catch-all ({*pageRoute}), che acquisiscono i percorsi tra più limiti di cartelle, vedere ASP.NET Core Blazor routing e spostamento.

Frammenti di rendering del contenuto figlio

I componenti possono impostare il contenuto di un altro componente. Il componente di assegnazione fornisce il contenuto tra i tag di apertura e chiusura del componente figlio.

Nell'esempio seguente il RenderFragmentChild componente ha un parametro componente che rappresenta un ChildContent segmento dell'interfaccia utente da eseguire come .RenderFragment La posizione del ChildContent markup del Razor componente è la posizione in cui viene eseguito il rendering del contenuto nell'output HTML finale.

Shared/RenderFragmentChild.razor:

<div class="card w-25" style="margin-bottom:15px">
    <div class="card-header font-weight-bold">Child content</div>
    <div class="card-body">@ChildContent</div>
</div>

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

Importante

La proprietà che riceve il RenderFragment contenuto deve essere denominata ChildContent per convenzione.

I callback degli eventi non sono supportati per RenderFragment.

Il componente seguente RenderFragmentParent fornisce contenuto per il rendering RenderFragmentChild del contenuto inserendo il contenuto all'interno dei tag di apertura e chiusura del componente figlio.

Pages/RenderFragmentParent.razor:

@page "/render-fragment-parent"

<h1>Render child content</h1>

<RenderFragmentChild>
    Content of the child component is supplied
    by the parent component.
</RenderFragmentChild>

A causa del Blazor rendering del contenuto figlio, i componenti di rendering all'interno di un for ciclo richiedono una variabile di indice locale se la variabile ciclo di incremento viene usata nel RenderFragmentChild contenuto del componente. L'esempio seguente può essere aggiunto al componente precedente RenderFragmentParent :

<h1>Three children with an index variable</h1>

@for (int c = 0; c < 3; c++)
{
    var current = c;

    <RenderFragmentChild>
        Count: @current
    </RenderFragmentChild>
}

In alternativa, usare un foreach ciclo con Enumerable.Range anziché un for ciclo. L'esempio seguente può essere aggiunto al componente precedente RenderFragmentParent :

<h1>Second example of three children with an index variable</h1>

@foreach (var c in Enumerable.Range(0,3))
{
    <RenderFragmentChild>
        Count: @c
    </RenderFragmentChild>
}

I frammenti di rendering vengono usati per eseguire il rendering del contenuto figlio in tutte Blazor le app e vengono descritti con esempi nelle sezioni di articoli e articoli seguenti:

Nota

BlazorI componenti predefiniti Razor di framework usano la stessa ChildContent convenzione dei parametri del componente per impostare il contenuto. È possibile visualizzare i componenti che impostano il contenuto figlio cercando il nome ChildContent della proprietà del parametro del componente nella documentazione dell'API (filtra l'API con il termine di ricerca "ChildContent").

Eseguire il rendering di frammenti per la logica di rendering riutilizzabile

È possibile considerare i componenti figlio puramente come modo per riutilizzare la logica di rendering. Nel blocco di qualsiasi componente definire un RenderFragment e eseguire il rendering del @code frammento da qualsiasi posizione quante volte necessario:

<h1>Hello, world!</h1>

@RenderWelcomeInfo

<p>Render the welcome info a second time:</p>

@RenderWelcomeInfo

@code {
    private RenderFragment RenderWelcomeInfo = __builder =>
    {
        <p>Welcome to your new app!</p>
    };
}

Per altre informazioni, vedere Riutilizzare la logica di rendering.

Parametri sovrascritti

Il framework impone in genere l'assegnazione Blazor di parametri padre-a-figlio sicuri:

  • I parametri non vengono sovrascritti in modo imprevisto.
  • Gli effetti collaterali sono ridotti al minimo. Ad esempio, vengono evitati altri rendering perché possono creare cicli di rendering infinito.

Un componente figlio riceve nuovi valori di parametro che potrebbero sovrascrivere i valori esistenti quando il componente padre esegue di nuovo il render. La sovrascrittura accidentale dei valori dei parametri in un componente figlio spesso si verifica quando si sviluppa il componente con uno o più parametri associati a dati e lo sviluppatore scrive direttamente in un parametro nel componente figlio:

  • Il rendering del componente figlio viene eseguito con uno o più valori di parametro dal componente padre.
  • Il figlio scrive direttamente sul valore di un parametro.
  • Il componente padre rerenders e sovrascrive il valore del parametro dell'elemento figlio.

Il potenziale di sovrascrittura dei valori dei parametri si estende anche nelle funzioni di accesso alle proprietà set del componente figlio.

Importante

Le linee guida generali non consentono di creare componenti che scrivono direttamente nei propri parametri dopo il rendering del componente per la prima volta.

Si consideri il componente seguente Expander :

  • Esegue il rendering del contenuto figlio.
  • Attiva/disattiva la visualizzazione del contenuto figlio con un parametro del componente (Expanded).

Dopo che il componente seguente Expander illustra un parametro sovrascritto, viene mostrato un componente modificato Expander per illustrare l'approccio corretto per questo scenario. Gli esempi seguenti possono essere inseriti in un'app di esempio locale per sperimentare i comportamenti descritti.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>Expanded</code> = @Expanded)</h2>

        @if (Expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    [Parameter]
    public bool Expanded { private get; set; }

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

    private void Toggle()
    {
        Expanded = !Expanded;
    }
}

Il Expander componente viene aggiunto al componente padre seguente ExpanderExample che può chiamare StateHasChanged:

Pages/ExpanderExample.razor:

@page "/expander-example"

<Expander Expanded="true">
    Expander 1 content
</Expander>

<Expander Expanded="true" />

<button @onclick="StateHasChanged">
    Call StateHasChanged
</button>

Inizialmente, i Expander componenti si comportano indipendentemente quando le relative Expanded proprietà vengono attivate o disattivate. I componenti figlio mantengono gli stati come previsto.

Se StateHasChanged viene chiamato in un componente padre, il Blazor framework rerendere i componenti figlio se i relativi parametri potrebbero essere stati modificati:

  • Per un gruppo di tipi di parametro che Blazor controlla in modo esplicito, Blazor esegue il rendering di un componente figlio se rileva che uno dei parametri è stato modificato.
  • Per i tipi di parametro non controllati, Blazor esegue nuovamente il ripristino del componente figlio indipendentemente dal fatto che i parametri siano stati modificati o meno. Il contenuto figlio rientra in questa categoria di tipi di parametro perché il contenuto figlio è di tipo RenderFragment, che è un delegato che fa riferimento ad altri oggetti modificabili.

Per il ExpanderExample componente:

  • Il primo Expander componente imposta il contenuto figlio in un oggetto potenzialmente modificabile RenderFragment, quindi una chiamata a StateHasChanged nel componente padre esegue automaticamente il rendering del componente e potenzialmente sovrascrive il valore di Expanded nel valore intitiale di true.
  • Il secondo Expander componente non imposta il contenuto figlio. Pertanto, un oggetto potenzialmente modificabile RenderFragment non esiste. Una chiamata a StateHasChanged nel componente padre non esegue automaticamente il rendere il componente figlio, quindi il valore del Expanded componente non viene sovrascritto.

Per mantenere lo stato nello scenario precedente, usare un campo privato nel Expander componente per mantenere lo stato attivato/disattivato.

Il componente modificato Expander seguente:

  • Accetta il valore del Expanded parametro del componente dall'elemento padre.
  • Assegna il valore del parametro del componente a un campo privato (expanded) nell'evento OnInitialized.
  • Usa il campo privato per mantenere lo stato di attivazione/disattivazione interna, che illustra come evitare di scrivere direttamente in un parametro.

Nota

Il consiglio in questa sezione si estende a una logica simile nelle funzioni di accesso ai parametri set dei componenti, che possono causare effetti collaterali indesiderati simili.

Shared/Expander.razor:

<div @onclick="Toggle" class="card bg-light mb-3" style="width:30rem">
    <div class="card-body">
        <h2 class="card-title">Toggle (<code>expanded</code> = @expanded)</h2>

        @if (expanded)
        {
            <p class="card-text">@ChildContent</p>
        }
    </div>
</div>

@code {
    private bool expanded;

    [Parameter]
    public bool Expanded { private get; set; }

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

    protected override void OnInitialized()
    {
        expanded = Expanded;
    }

    private void Toggle()
    {
        expanded = !expanded;
    }
}

Per altre informazioni, vedere Blazor Errore di associazione bidirezionale (dotnet/aspnetcore #24599).

Per altre informazioni sul rilevamento delle modifiche, vedere rendering dei componenti ASP.NET Core Razorinformazioni sui tipi esatti che Blazor controllano.

Splatting degli attributi e parametri arbitrari

I componenti possono acquisire ed eseguire il rendering di attributi aggiuntivi oltre ai parametri dichiarati del componente. È possibile acquisire attributi aggiuntivi in un dizionario e quindi eseguire il rendering in un elemento quando viene eseguito il rendering del componente usando l'attributo @attributesRazor direttiva . Questo scenario è utile per definire un componente che produce un elemento di markup che supporta un'ampia gamma di personalizzazioni. Ad esempio, può essere noioso definire gli attributi separatamente per un <input> oggetto che supporta molti parametri.

Nel componente seguente Splat :

  • Il primo <input> elemento (id="useIndividualParams") usa i singoli parametri del componente.
  • Il secondo <input> elemento (id="useAttributesDict") usa lo splatting degli attributi.

Pages/Splat.razor:

@page "/splat"

<input id="useIndividualParams"
       maxlength="@maxlength"
       placeholder="@placeholder"
       required="@required"
       size="@size" />

<input id="useAttributesDict"
       @attributes="InputAttributes" />

@code {
    private string maxlength = "10";
    private string placeholder = "Input placeholder text";
    private string required = "required";
    private string size = "50";

    private Dictionary<string, object> InputAttributes { get; set; } =
        new Dictionary<string, object>()
        {
            { "maxlength", "10" },
            { "placeholder", "Input placeholder text" },
            { "required", "required" },
            { "size", "50" }
        };
}

Gli elementi sottoposti <input> a rendering nella pagina Web sono identici:

<input id="useIndividualParams"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

<input id="useAttributesDict"
       maxlength="10"
       placeholder="Input placeholder text"
       required="required"
       size="50">

Per accettare attributi arbitrari, definire un parametro componente con la CaptureUnmatchedValues proprietà impostata su true:

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object> InputAttributes { get; set; }
}

La CaptureUnmatchedValues proprietà su [Parameter] consente al parametro di corrispondere a tutti gli attributi che non corrispondono ad altri parametri. Un componente può definire solo un singolo parametro con CaptureUnmatchedValues. Il tipo di proprietà usato con CaptureUnmatchedValues deve essere assegnabile da Dictionary<string, object> con chiavi stringa. L'uso di IEnumerable<KeyValuePair<string, object>> o IReadOnlyDictionary<string, object> sono anche opzioni in questo scenario.

La posizione di @attributes rispetto alla posizione degli attributi dell'elemento è importante. Quando @attributes viene eseguito lo splatted sull'elemento, gli attributi vengono elaborati da destra a sinistra (dall'ultimo al primo). Si consideri l'esempio seguente di un componente padre che utilizza un componente figlio:

Shared/AttributeOrderChild1.razor:

<div @attributes="AdditionalAttributes" extra="5" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object> AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent1.razor:

@page "/attribute-order-parent-1"

<AttributeOrderChild1 extra="10" />

L'attributo AttributeOrderChild1 del extra componente è impostato a destra di @attributes. Il AttributeOrderParent1 rendering <div> del componente contiene extra="5" quando viene passato l'attributo aggiuntivo perché gli attributi vengono elaborati da destra a sinistra (dall'ultimo al primo):

<div extra="5" />

Nell'esempio seguente l'ordine di extra e @attributes viene invertito nel componente <div>figlio:

Shared/AttributeOrderChild2.razor:

<div extra="5" @attributes="AdditionalAttributes" />

@code {
    [Parameter(CaptureUnmatchedValues = true)]
    public IDictionary<string, object> AdditionalAttributes { get; set; }
}

Pages/AttributeOrderParent2.razor:

@page "/attribute-order-parent-2"

<AttributeOrderChild2 extra="10" />

La <div> pagina Web di cui è stato eseguito il rendering del componente padre contiene extra="10" quando viene passato tramite l'attributo aggiuntivo:

<div extra="10" />

Acquisire riferimenti ai componenti

I riferimenti ai componenti consentono di fare riferimento a un'istanza del componente per l'emissione di comandi. Per acquisire un riferimento a un componente:

  • Aggiungere un @ref attributo al componente figlio.
  • Definire un campo con lo stesso tipo del componente figlio.

Quando viene eseguito il rendering del componente, il campo viene popolato con l'istanza del componente. È quindi possibile richiamare i metodi .NET nell'istanza di .

Si consideri il componente seguente ReferenceChild che registra un messaggio quando viene ChildMethod chiamato.

Shared/ReferenceChild.razor:

@using Microsoft.Extensions.Logging
@inject ILogger<ReferenceChild> logger

@code {
    public void ChildMethod(int value)
    {
        logger.LogInformation("Received {Value} in ChildMethod", value);
    }
}

Un riferimento al componente viene popolato solo dopo il rendering del componente e il relativo output include ReferenceChildl'elemento . Finché non viene eseguito il rendering del componente, non c'è nulla a cui fare riferimento.

Per modificare i riferimenti ai componenti al termine del rendering del componente, usare i OnAfterRender metodi o OnAfterRenderAsync.

Per usare una variabile di riferimento con un gestore eventi, usare un'espressione lambda o assegnare il delegato del gestore eventi nei OnAfterRender metodi o OnAfterRenderAsync. In questo modo si garantisce che la variabile di riferimento venga assegnata prima dell'assegnazione del gestore eventi.

L'approccio lambda seguente usa il componente precedente ReferenceChild .

Pages/ReferenceParent1.razor:

@page "/reference-parent-1"

<button @onclick="@(() => childComponent.ChildMethod(5))">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild childComponent;
}

L'approccio delegato seguente usa il componente precedente ReferenceChild .

Pages/ReferenceParent2.razor:

@page "/reference-parent-2"

<button @onclick="callChildMethod">
    Call <code>ReferenceChild.ChildMethod</code> with an argument of 5
</button>

<ReferenceChild @ref="childComponent" />

@code {
    private ReferenceChild childComponent;
    private Action callChildMethod;

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            callChildMethod = CallChildMethod;
        }
    }

    private void CallChildMethod()
    {
        childComponent.ChildMethod(5);
    }
}

Durante l'acquisizione dei riferimenti ai componenti viene usata una sintassi simile all'acquisizione dei riferimenti agli elementi, l'acquisizione dei riferimenti ai componenti non è una funzionalità di interoperabilità JavaScript. I riferimenti ai componenti non vengono passati al codice JavaScript. I riferimenti ai componenti vengono usati solo nel codice .NET.

Importante

Non usare i riferimenti ai componenti per modificare lo stato dei componenti figlio. Usare invece i normali parametri del componente dichiarativo per passare i dati ai componenti figlio. L'uso dei parametri del componente comporta componenti figlio che rerendere automaticamente i tempi corretti. Per altre informazioni, vedere la sezione relativa ai parametri del componente e l'articolo ASP.NET Core Blazor data binding.

Contesto di sincronizzazione

Blazor usa un contesto di sincronizzazione (SynchronizationContext) per applicare un singolo thread logico di esecuzione. I metodi del ciclo di vita di un componente e i callback degli eventi generati da Blazor vengono eseguiti nel contesto di sincronizzazione.

Blazor ServerIl contesto di sincronizzazione tenta di emulare un ambiente a thread singolo in modo che corrisponda perfettamente al modello WebAssembly nel browser, che è a thread singolo. In un determinato momento, il lavoro viene eseguito esattamente su un thread, che restituisce l'impressione di un singolo thread logico. Nessuna operazione viene eseguita simultaneamente.

Evitare chiamate di blocco dei thread

In genere, non chiamare i metodi seguenti nei componenti. I metodi seguenti bloccano il thread di esecuzione e quindi impediscono all'app di riprendere il lavoro fino al completamento dell'oggetto sottostante Task :

Nota

Blazor Esempi di documentazione che usano i metodi di blocco dei thread menzionati in questa sezione usano solo i metodi a scopo dimostrativo, non come indicazioni consigliate per la codifica. Ad esempio, alcune dimostrazioni di codice componente simulano un processo a esecuzione prolungata chiamando Thread.Sleep.

Richiamare i metodi dei componenti esternamente per aggiornare lo stato

Nel caso in cui un componente deve essere aggiornato in base a un evento esterno, ad esempio un timer o un'altra notifica, usare il metodo , che invia l'esecuzione InvokeAsync del codice al Blazorcontesto di sincronizzazione. Si consideri ad esempio il servizio di notifica seguente che può notificare a qualsiasi componente in ascolto lo stato aggiornato. Il Update metodo può essere chiamato da qualsiasi punto dell'app.

TimerService.cs:

using System;
using System.Timers;
using Microsoft.Extensions.Logging;

public class TimerService : IDisposable
{
    private int elapsedCount;
    private readonly ILogger<TimerService> logger;
    private readonly NotifierService notifier;
    private Timer timer;

    public TimerService(NotifierService notifier, ILogger<TimerService> logger)
    {
        this.notifier = notifier;
        this.logger = logger;
    }

    public void Start()
    {
        if (timer is null)
        {
            timer = new();
            timer.AutoReset = true;
            timer.Interval = 10000;
            timer.Elapsed += HandleTimer;
            timer.Enabled = true;
            logger.LogInformation("Started");
        }
    }

    private async void HandleTimer(object source, ElapsedEventArgs e)
    {
        elapsedCount += 1;
        await notifier.Update("elapsedCount", elapsedCount);
        logger.LogInformation($"elapsedCount: {elapsedCount}");
    }

    public void Dispose()
    {
        timer?.Dispose();
    }
}

NotifierService.cs:

using System;
using System.Threading.Tasks;

public class NotifierService
{
    public async Task Update(string key, int value)
    {
        if (Notify != null)
        {
            await Notify.Invoke(key, value);
        }
    }

    public event Func<string, int, Task> Notify;
}

Registrare i servizi:

  • In un'app Blazor WebAssembly registrare i servizi come singleton in Program.cs:

    builder.Services.AddSingleton<NotifierService>();
    builder.Services.AddSingleton<TimerService>();
    
  • In un'app Blazor Server registrare i servizi come con ambito in Startup.ConfigureServices:

    services.AddScoped<NotifierService>();
    services.AddScoped<TimerService>();
    

Usare per NotifierService aggiornare un componente.

Pages/ReceiveNotifications.razor:

@page "/receive-notifications"
@implements IDisposable
@inject NotifierService Notifier
@inject TimerService Timer

<h1>Receive Notifications</h1>

<h2>Timer Service</h2>

<button @onclick="StartTimer">Start Timer</button>

<h2>Notifications</h2>

<p>
    Status:
    @if (lastNotification.key is not null)
    {
        <span>@lastNotification.key = @lastNotification.value</span>
    }
    else
    {
        <span>Awaiting first notification</span>
    }
</p>

@code {
    private (string key, int value) lastNotification;

    protected override void OnInitialized()
    {
        Notifier.Notify += OnNotify;
    }

    public async Task OnNotify(string key, int value)
    {
        await InvokeAsync(() =>
        {
            lastNotification = (key, value);
            StateHasChanged();
        });
    }

    private void StartTimer()
    {
        Timer.Start();
    }

    public void Dispose()
    {
        Notifier.Notify -= OnNotify;
    }
}

Nell'esempio precedente:

  • NotifierService richiama il metodo del OnNotify componente all'esterno del contesto di Blazorsincronizzazione. InvokeAsync viene usato per passare al contesto e alla coda corretti di un rendering. Per altre informazioni, vedere rendering dei componenti ASP.NET CoreRazor.
  • Il componente implementa IDisposable. Il OnNotify delegato viene annullato nel Dispose metodo, chiamato dal framework quando il componente viene eliminato. Per altre informazioni, vedere ciclo di vita del componente ASP.NET CoreRazor.

Usare @key per controllare la conservazione di elementi e componenti

Quando si esegue il rendering di un elenco di elementi o componenti e gli elementi o i componenti successivamente cambiano, Blazor è necessario decidere quali elementi o componenti precedenti possono essere conservati e come gli oggetti modello devono essere mappati a loro. In genere, questo processo è automatico e può essere ignorato, ma esistono casi in cui è possibile controllare il processo.

Prendere in considerazione i componenti e People i seguentiDetails:

  • Il Details componente riceve i dati (Data) dal componente padre People , che viene visualizzato in un <input> elemento. Qualsiasi elemento visualizzato specificato <input> può ricevere lo stato attivo della pagina dall'utente quando selezionano uno degli <input> elementi.
  • Il People componente crea un elenco di oggetti persona per la visualizzazione usando il Details componente. Ogni tre secondi, una nuova persona viene aggiunta alla raccolta.

Questa dimostrazione consente di:

  • Selezionare un <input> oggetto tra diversi componenti di cui è stato eseguito Details il rendering.
  • Studiare il comportamento dello stato attivo della pagina man mano che la raccolta persone aumenta automaticamente.

Shared/Details.razor:

<input value="@Data" />

@code {
    [Parameter]
    public string Data { get; set; }
}

Nel componente seguente People ogni iterazione dell'aggiunta di una persona OnTimerCallback comporta la Blazor ricompilazione dell'intera raccolta. Lo stato attivo della pagina rimane sulla stessa posizione di indice degli <input> elementi, quindi lo stato attivo viene spostato ogni volta che viene aggiunta una persona. Spostando lo stato attivo lontano dal comportamento desiderato dell'utente selezionato. Dopo aver dimostrato il comportamento negativo con il componente seguente, l'attributo @key della direttiva viene usato per migliorare l'esperienza dell'utente.

Pages/People.razor:

@page "/people"
@using System.Timers
@implements IDisposable

@foreach (var person in people)
{
    <Details Data="@person.Data" />
}

@code {
    private Timer timer = new Timer(3000);

    public List<Person> people =
        new List<Person>()
        {
            { new Person { Data = "Person 1" } },
            { new Person { Data = "Person 2" } },
            { new Person { Data = "Person 3" } }
        };

    protected override void OnInitialized()
    {
        timer.Elapsed += (sender, eventArgs) => OnTimerCallback();
        timer.Start();
    }

    private void OnTimerCallback()
    {
        _ = InvokeAsync(() =>
        {
            people.Insert(0,
                new Person
                {
                    Data = $"INSERTED {DateTime.Now.ToString("hh:mm:ss tt")}"
                });
            StateHasChanged();
        });
    }

    public void Dispose() => timer.Dispose();

    public class Person
    {
        public string Data { get; set; }
    }
}

Il contenuto della people raccolta viene modificato con voci inserite, eliminate o riordinate. Il rerendering può causare differenze di comportamento visibili. Ogni volta che una persona viene inserita nella raccolta, l'elemento precedente dell'elementopeople attualmente incentrato riceve lo stato attivo. Lo stato attivo dell'utente viene perso.

Il processo di mapping di elementi o componenti a una raccolta può essere controllato con l'attributo @key direttiva. L'uso di garantisce la conservazione di @key elementi o componenti in base al valore della chiave. Se il Details componente nell'esempio precedente viene chiaveto nell'elemento person , Blazor ignora i componenti di rerendering Details che non sono stati modificati.

Per modificare il People componente per usare l'attributo @key di direttiva con la people raccolta, aggiornare l'elemento <Details> al seguente:

<Details @key="person" Data="@person.Data" />

Quando la raccolta cambia, l'associazione people tra Details istanze e person istanze viene mantenuta. Quando un Person oggetto viene inserito all'inizio della raccolta, una nuova Details istanza viene inserita in tale posizione corrispondente. Altre istanze vengono lasciate invariate. Pertanto, lo stato attivo dell'utente non viene perso perché le persone vengono aggiunte alla raccolta.

Altri aggiornamenti della raccolta presentano lo stesso comportamento quando viene usato l'attributo @key direttiva:

  • Se un'istanza viene eliminata dalla raccolta, viene rimossa solo l'istanza del componente corrispondente dall'interfaccia utente. Altre istanze vengono lasciate invariate.
  • Se le voci della raccolta vengono riordinate, le istanze del componente corrispondenti vengono mantenute e riordinate nell'interfaccia utente.

Importante

Le chiavi sono locali per ogni elemento o componente del contenitore. Le chiavi non vengono confrontate a livello globale nel documento.

Quando usare @key

In genere, è consigliabile usare @key ogni volta che viene eseguito il rendering di un elenco (ad esempio, in un foreach blocco) e esiste un valore appropriato per definire l'oggetto @key.

È anche possibile usare @key per mantenere un sottoalbero elemento o componente quando un oggetto non cambia, come illustrato negli esempi seguenti.

Esempio 1:

<li @key="person">
    <input value="@person.Data" />
</li>

Esempio 2:

<div @key="person">
    @* other HTML elements *@
</div>

Se un'istanza person cambia, la direttiva dell'attributo @key forza Blazor :

  • Eliminare l'intero <li> o <div> e i loro discendenti.
  • Ricompilare il sottoalbero all'interno dell'interfaccia utente con nuovi elementi e componenti.

Ciò è utile per garantire che non venga mantenuto alcun stato dell'interfaccia utente quando la raccolta cambia all'interno di un sottoalbero.

Ambito di @key

La direttiva dell'attributo @key è inclusa nell'ambito dei propri fratelli all'interno del relativo padre.

Si consideri l'esempio seguente. Le first chiavi e second vengono confrontate tra loro nello stesso ambito dell'elemento esterno <div> :

<div>
    <div @key="first">...</div>
    <div @key="second">...</div>
</div>

L'esempio seguente illustra first e second le chiavi nei propri ambiti, non correlate tra loro e senza influenza tra loro. Ogni @key ambito si applica solo al relativo elemento padre, non tra gli elementi padre <div><div> :

<div>
    <div @key="first">...</div>
</div>
<div>
    <div @key="second">...</div>
</div>

Per il componente illustrato in precedenza, gli esempi seguenti eseguono il Details rendering person dei dati nello stesso @key ambito e illustrano i casi d'uso tipici per @key:

<div>
    @foreach (var person in people)
    {
        <Details @key="person" Data="@person.Data" />
    }
</div>
@foreach (var person in people)
{
    <div @key="person">
        <Details Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li @key="person">
            <Details Data="@person.Data" />
        </li>
    }
</ol>

Negli esempi seguenti viene eseguito solo l'ambito @key dell'elemento <div> o <li> che circonda ogni Details istanza del componente. Pertanto, person i dati per ogni membro della people raccolta non vengono chiaveti in ogni person istanza nei componenti di cui è stato eseguito Details il rendering. Evitare i modelli seguenti quando si usa @key:

@foreach (var person in people)
{
    <div>
        <Details @key="person" Data="@person.Data" />
    </div>
}
<ol>
    @foreach (var person in people)
    {
        <li>
            <Details @key="person" Data="@person.Data" />
        </li>
    }
</ol>

Quando non usare @key

È previsto un costo delle prestazioni durante il rendering con @key. Il costo delle prestazioni non è elevato, ma specificare solo se conservare @key l'elemento o il componente beneficia dell'app.

Anche se @key non viene usato, Blazor mantiene le istanze dell'elemento figlio e del componente il più possibile. L'unico vantaggio da usare @keyè controllare il mapping delle istanze del modello alle istanze dei componenti mantenute anziché Blazor selezionare il mapping.

Valori da usare per @key

In genere, è consigliabile specificare uno dei valori seguenti per @key:

  • Istanze dell'oggetto modello. Ad esempio, l'istanza (person) è stata usata nell'esempio Person precedente. Ciò garantisce la conservazione in base all'uguaglianza dei riferimenti a oggetti.
  • Identificatori univoci. Ad esempio, gli identificatori univoci possono essere basati sui valori di chiave primaria di tipo int, stringo Guid.

Assicurarsi che i valori usati per @key non si scontrino. Se i valori di confronto vengono rilevati all'interno dello stesso elemento padre, Blazor genera un'eccezione perché non può eseguire il mapping deterministico di elementi o componenti precedenti a nuovi elementi o componenti. Usare solo valori distinti, ad esempio istanze di oggetti o valori chiave primaria.

Applicare un attributo

Gli attributi possono essere applicati ai componenti con la @attribute direttiva. Nell'esempio seguente viene applicato l'attributo[Authorize] alla classe del componente:

@page "/"
@attribute [Authorize]

Attributi degli elementi HTML condizionali

Le proprietà dell'attributo dell'elemento HTML vengono impostate in modo condizionale in base al valore .NET. Se il valore è false o null, la proprietà non è impostata. Se il valore è true, la proprietà è impostata.

Nell'esempio seguente determina IsCompleted se la <input> proprietà dell'elemento checked è impostata.

Pages/ConditionalAttribute.razor:

@page "/conditional-attribute"

<label>
    <input type="checkbox" checked="@IsCompleted" />
    Is Completed?
</label>

<button @onclick="@(() => IsCompleted = !IsCompleted)">
    Change IsCompleted
</button>

@code {
    [Parameter]
    public bool IsCompleted { get; set; }
}

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

Avviso

Alcuni attributi HTML, ad esempio aria-pressed, non funzionano correttamente quando il tipo .NET è un boologgetto . In questi casi, usare un string tipo anziché un boologgetto .

HTML non elaborato

Le stringhe vengono normalmente sottoposte a rendering usando nodi di testo DOM, il che significa che qualsiasi markup che possono contenere viene ignorato e considerato come testo letterale. Per eseguire il rendering di HTML non elaborato, eseguire il wrapping del contenuto HTML in un MarkupString valore. Il valore viene analizzato come HTML o SVG e inserito nel DOM.

Avviso

Il rendering di HTML non elaborato costruito da qualsiasi origine non attendibile è un rischio di sicurezza ed è sempre necessario evitare.

Nell'esempio seguente viene illustrato l'uso del tipo per aggiungere un blocco di contenuto HTML statico all'output MarkupString sottoposto a rendering di un componente.

Pages/MarkupStringExample.razor:

@page "/markup-string-example"

@((MarkupString)myMarkup)

@code {
    private string myMarkup =
        "<p class=\"text-danger\">This is a dangerous <em>markup string</em>.</p>";
}

Razor Modelli

È possibile definire frammenti di rendering usando Razor la sintassi del modello per definire un frammento di interfaccia utente. Razor i modelli usano il formato seguente:

@<{HTML tag}>...</{HTML tag}>

Nell'esempio seguente viene illustrato come specificare e visualizzare RenderFragment i RenderFragment<TValue> modelli direttamente in un componente. I frammenti di rendering possono essere passati anche come argomenti ai componenti modelli.

Pages/RazorTemplate.razor:

@page "/razor-template"

@timeTemplate

@petTemplate(new Pet { Name = "Nutty Rex" })

@code {
    private RenderFragment timeTemplate = @<p>The time is @DateTime.Now.</p>;
    private RenderFragment<Pet> petTemplate = (pet) => @<p>Pet: @pet.Name</p>;

    private class Pet
    {
        public string Name { get; set; }
    }
}

Output sottoposto a rendering del codice precedente:

<p>The time is 4/19/2021 8:54:46 AM.</p>
<p>Pet: Nutty Rex</p>

Asset statici

Blazorsegue la convenzione delle app ASP.NET Core per gli asset statici. Gli asset statici si trovano nella cartella () delweb root progetto owwwroot delle cartelle nella wwwroot cartella.

Usare un percorso relativo alla base (/) per fare riferimento alla radice Web per un asset statico. Nell'esempio seguente si logo.png trova fisicamente nella {PROJECT ROOT}/wwwroot/images cartella. {PROJECT ROOT} è la radice del progetto dell'app.

<img alt="Company logo" src="/images/logo.png" />

I componenti non supportano la notazione della barra tilde (~/).

Per informazioni sull'impostazione del percorso di base di un'app, vedere Host e distribuzione ASP.NET Core Blazor.

I helper tag non sono supportati nei componenti

Tag Helpers non sono supportati nei componenti. Per fornire funzionalità simili a Tag helper in Blazor, creare un componente con la stessa funzionalità dell'helper tag e usare invece il componente.

Immagini vettoriali scalabili (SVG)

Poiché Blazor esegue il rendering di immagini HTML, supportate dal browser, incluse le immagini SVG (Scalable Vector Graphics) (.svg), sono supportate tramite il <img> tag:

<img alt="Example image" src="image.svg" />

Analogamente, le immagini SVG sono supportate nelle regole CSS di un file del foglio di stile (.css):

.element-class {
    background-image: url("image.svg");
}

Comportamento di rendering degli spazi vuoti

Lo spazio vuoto viene mantenuto nel markup di origine di un componente. Lo spazio vuoto esegue il rendering del testo solo nel DOM del browser anche quando non è presente alcun effetto visivo.

Prendere in considerazione il markup del componente seguente:

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

Nell'esempio precedente viene eseguito il rendering degli spazi vuoti non necessari seguenti:

  • All'esterno del blocco di @foreach codice.
  • Intorno all'elemento <li> .
  • Intorno all'output @item.Text .

Un elenco di 100 elementi comporta oltre 400 aree di spazi vuoti. Nessuno degli spazi vuoti aggiuntivi influisce visivamente sull'output sottoposto a rendering.

Quando si esegue il rendering di HTML statici per i componenti, lo spazio vuoto all'interno di un tag non viene mantenuto. Ad esempio, visualizzare l'output di rendering del tag seguente <img> in un file del componente Razor (.razor):

<img     alt="Example image"   src="img.png"     />

Lo spazio vuoto non viene mantenuto dal markup precedente:

<img alt="Example image" src="img.png" />

Supporto dei parametri di tipo generico

La @typeparam direttiva dichiara un parametro di tipo generico per la classe componente generata:

@typeparam TItem

Nell'esempio seguente il ListGenericTypeItems1 componente viene genericamente digitato come TExample.

Shared/ListGenericTypeItems1.razor:

@typeparam TExample

@if (ExampleList != null)
{
    <ul>
        @foreach (var item in ExampleList)
        {
            <li>@item</li>
        }
    </ul>
}

@code {
    [Parameter]
    public IEnumerable<TExample> ExampleList{ get; set; }
}

Il componente seguente GenericTypeExample1 esegue il rendering di due ListGenericTypeItems1 componenti:

  • I dati stringa o integer vengono assegnati al ExampleList parametro di ogni componente.
  • Tipo string o int corrispondente al tipo dei dati assegnati viene impostato per il parametro di tipo (TExample) di ogni componente.

Pages/GenericTypeExample1.razor:

@page "/generic-type-example-1"

<h1>Generic Type Example 1</h1>

<ListGenericTypeItems1 ExampleList="@(new List<string> { "Item 1", "Item 2" })" 
                       TExample="string" />

<ListGenericTypeItems1 ExampleList="@(new List<int> { 1, 2, 3 })" 
                       TExample="int" />

Per altre informazioni, vedere gli articoli seguenti: