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 ilProductDetail
componente ha un nome file diProductDetail.razor
e risiede nellaPages
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-detail
relativo .
† 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-world
relativo . 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 valorefont-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 void
dei 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-example
in .
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
@using
le 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 diBlazorSample
(BlazorSample.csproj
) nello spazio dei nomiBlazorSample.Pages
per ilIndex
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'ambitoIndex
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.
- Nella stessa cartella ,
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 alNavMenu
componente nella cartella dell'appShared
(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++;
}
}
}
@using
le 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 perTitle
eBody
dalParameterParent
componente, che usa un'espressione C# esplicita per impostare i valori dellePanelBody
proprietà 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:
- Valore del
title
campo. - Risultato del
GetTitle
metodo C#. - Data locale corrente in formato lungo con ToLongDateString, che usa un'espressione C# implicita.
- Proprietà
panelData
dell'oggettoTitle
.
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"
, dovetitle
è una variabile tipizzata di stringa.Count="@ct"
, dovect
è 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 GetTitle
C# :
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 Tuple
oggetto :
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:
- Blazor Layout
- Passare i dati in una gerarchia di componenti
- Componenti basati su modelli
- Gestione globale delle eccezioni
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:
- La chiamata StateHasChanged nel codice per sviluppatori notifica a un componente che lo stato è cambiato e attiva in genere il rerendering dei componenti per aggiornare l'interfaccia utente. StateHasChangedviene descritto più dettagliatamente più avanti nel ciclo di vita del componente ASP.NET Core Razor e nel rendering dei componenti ASP.NET CoreRazor.
- L'attributo della direttiva del
@onclick
pulsante collega un gestore eventi all'evento delonclick
pulsante. La gestione degli eventi viene descritta in modo più dettagliato più avanti in ASP.NET Core Blazor gestione degli eventi.
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 diExpanded
al relativo valore intitiale ditrue
. - 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 delExpanded
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 @attributes
Razor 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 ReferenceChild
l'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 delOnNotify
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 nelDispose
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 padrePeople
, 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 ilDetails
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 eseguitoDetails
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
,string
oGuid
.
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 unvalue
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 , true
lo 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
oint
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
, TValue
e 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;
nelProgram.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;
nelProgram.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;
nelProgram.cs
file.In un'app Blazor WebAssembly chiamare
RegisterAsCustomElement
RootComponents inProgram.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;
nelProgram.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 ilProductDetail
componente ha un nome file diProductDetail.razor
e risiede nellaPages
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-detail
relativo .
†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 @page
RouteAttribute 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-world
relativo. 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 valorefont-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 void
dei 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
@using
le 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 diBlazorSample
(BlazorSample.csproj
) nello spazio dei nomiBlazorSample.Pages
per ilIndex
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'ambitoIndex
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.
- Nella stessa cartella ,
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 alNavMenu
componente nella cartella dell'appShared
(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++;
}
}
}
@using
le 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 perTitle
eBody
dalParameterParent
componente, che usa un'espressione C# esplicita per impostare i valori dellePanelBody
proprietà 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:
- Valore del
title
campo. - Risultato del
GetTitle
metodo C#. - Data locale corrente in formato lungo con ToLongDateString, che usa un'espressione C# implicita.
- Proprietà
panelData
dell'oggettoTitle
.
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"
, dovetitle
è una variabile tipizzata da stringa.Count="@ct"
, dovect
è 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 GetTitle
C#:
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:
- Blazor Layout
- Passare i dati in una gerarchia di componenti
- Componenti basati su modelli
- Gestione globale delle eccezioni
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:
- La chiamata StateHasChanged nel codice per sviluppatori notifica a un componente che lo stato è cambiato e attiva in genere il rerendering dei componenti per aggiornare l'interfaccia utente. StateHasChangedviene descritto più dettagliatamente più avanti nel ciclo di vita del componente ASP.NET Core Razor e nel rendering dei componenti ASP.NET CoreRazor.
- L'attributo della direttiva del
@onclick
pulsante collega un gestore eventi all'evento delonclick
pulsante. La gestione degli eventi viene descritta in modo più dettagliato più avanti in ASP.NET Core Blazor gestione degli eventi.
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 diExpanded
al relativo valore intitiale ditrue
. - 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 delExpanded
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 @attributes
Razor 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 ReferenceChild
l'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 delOnNotify
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 nelDispose
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 padrePeople
, 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 ilDetails
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 eseguitoDetails
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'esempioPerson
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
,string
oGuid
.
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 bool
oggetto . In questi casi, usare un string
tipo anziché un bool
oggetto .
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
oint
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 ilProductDetail
componente ha un nome file diProductDetail.razor
e risiede nellaPages
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-detail
relativo .
†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 @page
RouteAttribute 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-world
relativo. 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 valorefont-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 void
dei 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-example
in .
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
@using
le 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 diBlazorSample
(BlazorSample.csproj
) nello spazio dei nomiBlazorSample.Pages
per ilIndex
componente.{PROJECT ROOT}
è il percorso radice del progetto. I componenti seguono le regole di associazione dei nomi C#. Per ilIndex
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.
- Nella stessa cartella ,
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 alNavMenu
componente nella cartella dell'appShared
(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++;
}
}
}
@using
le 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 perTitle
eBody
dalParameterParent
componente, che usa un'espressione C# esplicita per impostare i valori dellePanelBody
proprietà 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:
- Valore del
title
campo. - Risultato del
GetTitle
metodo C#. - Data locale corrente in formato lungo con ToLongDateString, che usa un'espressione C# implicita.
- Proprietà
panelData
dell'oggettoTitle
.
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"
, dovetitle
è una variabile tipizzata di stringa.Count="@ct"
, dovect
è 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 GetTitle
C# :
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:
- Blazor Layout
- Passare i dati in una gerarchia di componenti
- Componenti basati su modelli
- Gestione globale delle eccezioni
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:
- La chiamata StateHasChanged nel codice dello sviluppatore notifica a un componente che il relativo stato è stato modificato e in genere attiva il rerendering dei componenti per aggiornare l'interfaccia utente. StateHasChangedviene descritto in modo più dettagliato più avanti nel ciclo di vita dei componenti ASP.NET Core Razor e nel rendering dei componenti ASP.NET CoreRazor.
- L'attributo di direttiva del
@onclick
pulsante associa un gestore eventi all'evento delonclick
pulsante. La gestione degli eventi viene descritta in modo più dettagliato più avanti in ASP.NET Core Blazor gestione degli eventi.
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 diExpanded
nel valore intitiale ditrue
. - 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 delExpanded
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'eventoOnInitialized
. - 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 @attributes
Razor 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 ReferenceChild
l'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 delOnNotify
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 nelDispose
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 padrePeople
, 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 ilDetails
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 eseguitoDetails
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'esempioPerson
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
,string
oGuid
.
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 bool
oggetto . In questi casi, usare un string
tipo anziché un bool
oggetto .
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
oint
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: