Blazor data binding ASP.NET Core
Questo articolo illustra le funzionalità di data binding per Razor i componenti e gli elementi DOM (Document Object Model) nelle Blazor app.
Razor i componenti forniscono funzionalità di data binding con l'attributo @bind
Razor di direttiva con un campo, una proprietà o Razor un valore di espressione.
L'esempio seguente esegue l'associazione:
- Valore
<input>
dell'elemento per il campo C#inputValue
. <input>
Secondo valore dell'elemento per la proprietà C#InputValue
.
Quando un <input>
elemento perde lo stato attivo, viene aggiornato il campo o la proprietà associata.
Pages/Bind.razor
:
@page "/bind"
<p>
<input @bind="inputValue" />
</p>
<p>
<input @bind="InputValue" />
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string? inputValue;
private string? InputValue { get; set; }
}
La casella di testo viene aggiornata nell'interfaccia utente solo quando viene eseguito il rendering del componente, non in risposta alla modifica del valore del campo o della proprietà. Poiché i componenti vengono visualizzati dopo l'esecuzione del codice del gestore eventi, gli aggiornamenti dei campi e delle proprietà vengono in genere riflessi nell'interfaccia utente immediatamente dopo l'attivazione di un gestore eventi.
Come dimostrazione della composizione del data binding in HTML, nell'esempio seguente la InputValue
proprietà viene associata agli attributi e onchange
del value
secondo <input>
elemento (change
). Il secondo <input>
elemento nell'esempio seguente è una dimostrazione di concetto e non è progettato per suggerire come associare i dati nei Razor componenti.
Pages/BindTheory.razor
:
@page "/bind-theory"
<p>
<label>
Normal Blazor binding:
<input @bind="InputValue" />
</label>
</p>
<p>
<label>
Demonstration of equivalent HTML binding:
<input value="@InputValue"
@onchange="@((ChangeEventArgs __e) => InputValue = __e?.Value?.ToString())" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
Quando viene eseguito il rendering del BindTheory
componente, l'oggetto value
dell'elemento dimostrativo <input>
HTML proviene dalla InputValue
proprietà . Quando l'utente immette un valore nella casella di testo e modifica lo stato attivo dell'elemento, l'evento onchange
viene generato e la InputValue
proprietà viene impostata sul valore modificato. In realtà, l'esecuzione del codice è più complessa perché @bind
gestisce i casi in cui vengono eseguite le conversioni dei tipi. In generale, @bind
associa il valore corrente di un'espressione all'attributo value
di <input>
e gestisce le modifiche usando il gestore registrato.
Associare una proprietà o un campo su altri eventi DOM (Document Object Model) includendo un attributo con un @bind:event="{EVENT}"
evento DOM per il {EVENT}
segnaposto. Nell'esempio seguente la InputValue
proprietà viene associata al <input>
valore dell'elemento quando viene attivato l'evento dell'elemento oninput
(input
). A differenza dell'evento onchange
(change
), che viene generato quando l'elemento perde lo stato attivo, oninput
(input
) viene attivato quando il valore della casella di testo cambia.
Page/BindEvent.razor
:
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
Per eseguire la logica asincrona dopo l'associazione, usare @bind:after="{EVENT}"
con un evento DOM per il {EVENT}
segnaposto. Un metodo C# assegnato non viene eseguito finché il valore associato non viene assegnato in modo sincrono.
L'uso di un parametro di callback di eventi ([Parameter] public EventCallback<string> ValueChanged { get; set; }
) con @bind:after
non è supportato. Passare invece un metodo che restituisce un oggetto Action o Task a @bind:after
.
Nell'esempio seguente:
- L'elemento
<input>
value
è associato al valore disearchText
in modo sincrono. - Dopo ogni sequenza di tasti (
onchange
evento) nel campo, ilPerformSearch
metodo viene eseguito in modo asincrono. PerformSearch
chiama un servizio con un metodo asincrono (FetchAsync
) per restituire i risultati della ricerca.
@inject ISearchService SearchService
<input @bind="searchText" @bind:after="PerformSearch" />
@code {
private string? searchText;
private string[]? searchResult;
private async Task PerformSearch()
{
searchResult = await SearchService.FetchAsync(searchText);
}
}
Esempi aggiuntivi
Pages/BindAfter.razor
:
@page "/bind-after"
@using Microsoft.AspNetCore.Components.Forms
<h1>Bind After Examples</h1>
<h2>Elements</h2>
<input type="text" @bind="text" @bind:after="() => { }" />
<input type="text" @bind="text" @bind:after="After" />
<input type="text" @bind="text" @bind:after="AfterAsync" />
<h2>Components</h2>
<InputText @bind-Value="text" @bind-Value:after="() => { }" />
<InputText @bind-Value="text" @bind-Value:after="After" />
<InputText @bind-Value="text" @bind-Value:after="AfterAsync" />
@code {
private string text = "";
private void After() {}
private Task AfterAsync() { return Task.CompletedTask; }
}
Per altre informazioni sul InputText
componente, vedere ASP.NET Core Blazor moduli e componenti di input.
I componenti supportano il data binding bidirezionale definendo una coppia di parametri:
@bind:get
: specifica il valore da associare.@bind:set
: specifica un callback per quando il valore viene modificato.
I @bind:get
modificatori e @bind:set
vengono sempre usati insieme.
L'uso di un parametro di callback di eventi con @bind:set
([Parameter] public EventCallback<string> ValueChanged { get; set; }
) non è supportato. Passare invece un metodo che restituisce un oggetto Action o Task a @bind:set
.
Esempio
Pages/BindGetSet.razor
:
@page "/bind-get-set"
@using Microsoft.AspNetCore.Components.Forms
<h1>Bind Get Set Examples</h1>
<h2>Elements</h2>
<input type="text" @bind:get="text" @bind:set="(value) => { text = value; }" />
<input type="text" @bind:get="text" @bind:set="Set" />
<input type="text" @bind:get="text" @bind:set="SetAsync" />
<h2>Components</h2>
<InputText @bind-Value:get="text" @bind-Value:set="(value) => { text = value; }" />
<InputText @bind-Value:get="text" @bind-Value:set="Set" />
<InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />
@code {
private string text = "";
private void Set(string value)
{
text = value;
}
private Task SetAsync(string value)
{
text = value;
return Task.CompletedTask;
}
}
Per altre informazioni sul InputText
componente, vedere ASP.NET Core Blazor moduli e componenti di input.
Razor l'associazione di attributi fa distinzione tra maiuscole e minuscole:
@bind
,@bind:event
e@bind:after
sono validi.@Bind
/@bind:Event
/@bind:aftEr
(lettere maiuscole) o@BIND
/@BIND:AFTER
/@BIND:EVENT
(tutte le lettere maiuscole) non sono valide.
Usare @bind:get
/@bind:set
modificatori ed evitare gestori eventi per il data binding bidirezionale
Il data binding bidirezionale non è possibile implementare con un gestore eventi. Usare @bind:get
/@bind:set
i modificatori per il data binding bidirezionale.
Si consideri l'approccio disfunzionale seguente per il data binding bidirezionale usando un gestore eventi:
<p>
<input value="@inputValue" @oninput="OnInput" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private string? inputValue;
private void OnInput(ChangeEventArgs args)
{
var newValue = args.Value?.ToString() ?? string.Empty;
inputValue = newValue.Length > 4 ? "Long!" : newValue;
}
}
Il OnInput
gestore eventi aggiorna il valore di inputValue
su Long!
dopo che viene fornito un quarto carattere. Tuttavia, l'utente può continuare ad aggiungere caratteri al valore dell'elemento nell'interfaccia utente. Il valore di inputValue
non è associato al valore dell'elemento con ogni sequenza di tasti. L'esempio precedente è in grado di eseguire il data binding unidirezionale.
Il motivo di questo comportamento è che Blazor non è consapevole del fatto che il codice intende modificare il valore di inputValue
nel gestore eventi. Blazor non tenta di forzare i valori degli elementi DOM (Document Object Model) e i valori delle variabili .NET in modo che corrispondano a meno che non siano associati alla @bind
sintassi. Nelle versioni precedenti di Blazor, il data binding bidirezionale viene implementato associando l'elemento a una proprietà e controllando il valore della proprietà con il relativo setter. In ASP.NET Core 7.0 o versione successiva viene @bind:get
/@bind:set
usata la sintassi del modificatore per implementare il data binding bidirezionale, come illustrato nell'esempio seguente.
@bind:get
/@bind:set
per il data binding bidirezionale:
<p>
<input @bind:event="oninput" @bind:get="inputValue" @bind:set="OnInput" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private string? inputValue;
private void OnInput(string value)
{
var newValue = value ?? string.Empty;
inputValue = newValue.Length > 4 ? "Long!" : newValue;
}
}
L'uso dei @bind:get
/@bind:set
modificatori controlla entrambi il valore sottostante di inputValue
tramite @bind:set
e associa il valore di inputValue
al valore dell'elemento tramite .@bind:get
Nell'esempio precedente viene illustrato l'approccio corretto per l'implementazione del data binding bidirezionale.
Associazione a una proprietà con C# get
e set
funzioni di accesso
Le funzioni di accesso e set
C# get
possono essere usate per creare un comportamento di formato di associazione personalizzato, come illustrato nel componente seguenteDecimalBinding
. Il componente associa un decimale positivo o negativo con un massimo di tre posizioni decimali a un <input>
elemento tramite una string
proprietà (DecimalValue
).
Pages/DecimalBinding.razor
:
@page "/decimal-binding"
@using System.Globalization
<p>
<label>
Decimal value (±0.000 format):
<input @bind="DecimalValue" />
</label>
</p>
<p>
<code>decimalValue</code>: @decimalValue
</p>
@code {
private decimal decimalValue = 1.1M;
private NumberStyles style =
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string DecimalValue
{
get => decimalValue.ToString("0.000", culture);
set
{
if (Decimal.TryParse(value, style, culture, out var number))
{
decimalValue = Math.Round(number, 3);
}
}
}
}
Nota
L'associazione bidirezionale a una proprietà con get
/set
funzioni di accesso richiede l'eliminazione dell'oggetto Task restituito da .EventCallback.InvokeAsync Per il data binding bidirezionale in ASP.NET Core 7.0 o versione successiva, è consigliabile usare @bind:get
/@bind:set
i modificatori. Per altre informazioni, vedere le @bind:get
/@bind:set
linee guida nella sezione di apertura di questo articolo.
Selezione di più opzioni con <select>
elementi
L'associazione supporta la multiple
selezione di opzioni con <select>
elementi. L'evento @onchange
fornisce una matrice degli elementi selezionati tramite argomenti evento (ChangeEventArgs
). Il valore deve essere associato a un tipo di matrice.
Pages/BindMultipleInput.razor
:
@page "/bind-multiple-input"
<h1>Bind Multiple <code>input</code>Example</h1>
<p>
<label>
Select one or more cars:
<select @onchange="SelectedCarsChanged" multiple>
<option value="audi">Audi</option>
<option value="jeep">Jeep</option>
<option value="opel">Opel</option>
<option value="saab">Saab</option>
<option value="volvo">Volvo</option>
</select>
</label>
</p>
<p>
Selected Cars: @string.Join(", ", SelectedCars)
</p>
<p>
<label>
Select one or more cities:
<select @bind="SelectedCities" multiple>
<option value="bal">Baltimore</option>
<option value="la">Los Angeles</option>
<option value="pdx">Portland</option>
<option value="sf">San Francisco</option>
<option value="sea">Seattle</option>
</select>
</label>
</p>
<span>
Selected Cities: @string.Join(", ", SelectedCities)
</span>
@code {
public string[] SelectedCars { get; set; } = new string[] { };
public string[] SelectedCities { get; set; } = new[] { "bal", "sea" };
private void SelectedCarsChanged(ChangeEventArgs e)
{
if (e.Value is not null)
{
SelectedCars = (string[])e.Value;
}
}
}
Per informazioni sulla modalità di gestione delle stringhe e dei valori vuoti nel data binding, vedere la sezione Binding element options to C# object values .For information on how empty strings and null
values are handled in data binding, see the Binding <select>
element options to C# object null
values section.
Opzioni dell'elemento di <select>
associazione ai valori dell'oggetto null
C#
Non esiste un modo ragionevole per rappresentare un <select>
valore dell'opzione elemento come valore dell'oggetto null
C#, perché:
- Gli attributi HTML non possono avere
null
valori. L'equivalente più vicino anull
in HTML è l'assenza dell'attributo HTMLvalue
dall'elemento<option>
. - Quando si seleziona un
<option>
oggetto senzavalue
attributo, il browser considera il valore come contenuto di testo dell'elemento<option>
.
Il Blazor framework non tenta di eliminare il comportamento predefinito perché comporta:
- Creazione di una catena di soluzioni alternative speciali nel framework.
- Modifiche di rilievo al comportamento corrente del framework.
L'equivalente più plausibile null
in HTML è una stringavalue
vuota. Il Blazor framework gestisce null
le conversioni di stringhe vuote per l'associazione bidirezionale al valore di un <select>
.
Valori non analizzabili
Quando un utente fornisce un valore non verificabile a un elemento databound, il valore non analizzabile viene ripristinato automaticamente al valore precedente quando viene attivato l'evento di associazione.
Si consideri il componente seguente, in cui un <input>
elemento è associato a un int
tipo con un valore iniziale di 123
.
Pages/UnparsableValues.razor
:
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
Per impostazione predefinita, l'associazione si applica all'evento dell'elemento onchange
. Se l'utente aggiorna il valore della voce della casella di testo in 123.45
e modifica lo stato attivo, il valore dell'elemento viene ripristinato 123
quando onchange
viene attivato. Quando il valore 123.45
viene rifiutato a favore del valore originale di 123
, l'utente riconosce che il valore non è stato accettato.
Per l'evento oninput
(@bind:event="oninput"
), si verifica una riversione del valore dopo qualsiasi sequenza di tasti che introduce un valore non verificabile. Quando si specifica come destinazione l'evento oninput
con un int
tipo associato a , un utente non può digitare un carattere punto (.
). Un carattere punto (.
) viene immediatamente rimosso, quindi l'utente riceve un feedback immediato che è consentito solo per numeri interi. Esistono scenari in cui il ripristino del valore sull'evento oninput
non è ideale, ad esempio quando l'utente deve essere autorizzato a cancellare un valore non verificabile <input>
. Le alternative includono:
- Non usare l'evento
oninput
. Usare l'evento predefinitoonchange
, in cui un valore non valido non viene ripristinato fino a quando l'elemento non perde lo stato attivo. - Eseguire il binding a un tipo nullable, ad esempio o e usare
@bind:get
@bind:set
/modificatori (descritti in precedenza in questo articolo) o eseguire l'associazione a una proprietà con logica personalizzataget
eset
della funzione di accesso per gestire voci non valide.string
int?
- Usare un componente di convalida dei moduli, ad esempio InputNumber<TValue> o .InputDate<TValue> I componenti di convalida dei moduli forniscono il supporto predefinito per gestire gli input non validi. Componenti di convalida dei moduli:
- Consentire all'utente di fornire input non valido e ricevere errori di convalida nell'oggetto associato EditContext.
- Visualizza gli errori di convalida nell'interfaccia utente senza interferire con l'utente immettendo dati webform aggiuntivi.
Stringhe di formato
Il data binding funziona con una singola DateTime stringa di formato usando @bind:format="{FORMAT STRING}"
, dove il {FORMAT STRING}
segnaposto è la stringa di formato. Altre espressioni di formato, ad esempio i formati valuta o numero, non sono disponibili in questo momento, ma potrebbero essere aggiunte in una versione futura.
Pages/DateBinding.razor
:
@page "/date-binding"
<p>
<label>
<code>yyyy-MM-dd</code> format:
<input @bind="startDate" @bind:format="yyyy-MM-dd" />
</label>
</p>
<p>
<code>startDate</code>: @startDate
</p>
@code {
private DateTime startDate = new(2020, 1, 1);
}
Nel codice precedente il <input>
tipo di campo dell'elemento (type
attributo) è predefinito su text
.
Nullable System.DateTime e System.DateTimeOffset sono supportati:
private DateTime? date;
private DateTimeOffset? dateOffset;
Se si specifica un formato per il date
tipo di campo, non è consigliabile perché Blazor è disponibile il supporto predefinito per formattare le date. Nonostante la raccomandazione, usare solo il formato data per l'associazione yyyy-MM-dd
per funzionare correttamente se viene fornito un formato con il date
tipo di campo:
<input type="date" @bind="startDate" @bind:format="yyyy-MM-dd">
Associazione con parametri del componente
Uno scenario comune associa una proprietà di un componente figlio a una proprietà nel relativo componente padre. Questo scenario viene chiamato associazione concatenata perché si verificano simultaneamente più livelli di associazione.
I parametri del componente consentono le proprietà di associazione di un componente padre con @bind-{PROPERTY}
sintassi, in cui il {PROPERTY}
segnaposto è la proprietà da associare.
Non è possibile implementare associazioni concatenati con @bind
sintassi nel componente figlio. Un gestore eventi e un valore devono essere specificati separatamente per supportare l'aggiornamento della proprietà nel componente padre dal componente figlio.
Il componente padre sfrutta ancora la @bind
sintassi per configurare l'associazione dei dati con il componente figlio.
Il componente seguente ChildBind
include un Year
parametro componente e un EventCallback<TValue>oggetto . Per convenzione, il EventCallback<TValue> parametro per il parametro deve essere denominato come nome del parametro del componente con un suffisso "Changed
". La sintassi di denominazione è , dove il segnaposto è {PARAMETER NAME}Changed
il {PARAMETER NAME}
nome del parametro. Nell'esempio seguente viene EventCallback<TValue> denominato YearChanged
.
EventCallback.InvokeAsync richiama il delegato associato all'associazione con l'argomento specificato e invia una notifica evento per la proprietà modificata.
Shared/ChildBind.razor
:
<div class="card bg-light mt-3" style="width:18rem ">
<div class="card-body">
<h3 class="card-title">ChildBind Component</h3>
<p class="card-text">
Child <code>Year</code>: @Year
</p>
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
</div>
</div>
@code {
private Random r = new();
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild()
{
await YearChanged.InvokeAsync(r.Next(1950, 2021));
}
}
Per altre informazioni sugli eventi e EventCallback<TValue>, vedere la sezione EventCallback dell'articolo sulla gestione degli eventi ASP.NET CoreBlazor.
Nel componente seguente Parent1
il year
campo è associato al Year
parametro del componente figlio. Il Year
parametro è associabile perché ha un evento complementare YearChanged
che corrisponde al tipo del Year
parametro.
Pages/Parent1.razor
:
@page "/parent-1"
<h1>Parent Component</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private Random r = new();
private int year = 1979;
private void UpdateYear()
{
year = r.Next(1950, 2021);
}
}
L'associazione dei parametri del componente può anche attivare @bind:after
eventi. Nell'esempio seguente il YearUpdated
metodo viene eseguito in modo asincrono dopo l'associazione del Year
parametro del componente.
<ChildBind @bind-Year="year" @bind-Year:after="YearUpdated" />
@code {
...
private async Task YearUpdated()
{
... = await ...;
}
}
Per convenzione, una proprietà può essere associata a un gestore eventi corrispondente includendo un @bind-{PROPERTY}:event
attributo assegnato al gestore, dove il {PROPERTY}
segnaposto è la proprietà. <ChildBind @bind-Year="year" />
equivale alla scrittura:
<ChildBind @bind-Year="year" @bind-Year:event="YearChanged" />
In un esempio più sofisticato e reale, il componente seguente PasswordEntry
:
- Imposta il valore di un
<input>
elemento su unpassword
campo. - Espone le modifiche di una proprietà a un
Password
componente padre con unEventCallback
oggetto che passa il valore corrente del campo figliopassword
come argomento. - Usa l'evento per attivare il
onclick
ToggleShowPassword
metodo. Per altre informazioni, vedere gestione degli eventi ASP.NET CoreBlazor.
Shared/PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
Il PasswordEntry
componente viene usato in un altro componente, ad esempio l'esempio di componente seguente PasswordBinding
.
Pages/PasswordBinding.razor
:
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
Quando il componente viene inizialmente eseguito il PasswordBinding
rendering, il password
valore di Not set
viene visualizzato nell'interfaccia utente. Dopo il rendering iniziale, il valore di password
riflette le modifiche apportate al Password
valore del parametro del componente nel PasswordEntry
componente.
Nota
L'esempio precedente associa la password unidirezionale dal componente figlio PasswordEntry
al componente padre PasswordBinding
. L'associazione bidirezionale non è un requisito in questo scenario se l'obiettivo è che l'app disponga di un componente di immissione della password condivisa per il riutilizzo dell'app che passa semplicemente la password all'elemento padre. Per un approccio che consente l'associazione bidirezionale senza scrivere direttamente nel parametro del componente figlio, vedere l'esempioNestedChild
del componente nella sezione Bind in più di due componenti di questo articolo.
Eseguire controlli o errori di trappola nel gestore. Il componente modificato PasswordEntry
seguente fornisce commenti e suggerimenti immediati all'utente se viene usato uno spazio nel valore della password.
Shared/PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
private string? validationMessage;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
if (password != null && password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
Nell'esempio seguente il PasswordUpdated
metodo viene eseguito in modo asincrono dopo l'associazione del Password
parametro del componente:
<PasswordEntry @bind-Password="password" @bind-Password:after="PasswordUpdated" />
Associare più di due componenti
È possibile associare i parametri tramite qualsiasi numero di componenti annidati, ma è necessario rispettare il flusso unidirezionale dei dati:
- Le notifiche di modifica passano alla gerarchia.
- I nuovi valori dei parametri passano verso il basso nella gerarchia.
Un approccio comune e consigliato consiste nell'archiviare solo i dati sottostanti nel componente padre per evitare qualsiasi confusione sullo stato da aggiornare, come illustrato nell'esempio seguente.
Pages/Parent2.razor
:
@page "/parent-2"
<h1>Parent Component</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue()
{
parentMessage = $"Set in Parent {DateTime.Now}";
}
}
Nel componente seguente NestedChild
, il NestedGrandchild
componente:
- Assegna il valore di
ChildMessage
aGrandchildMessage
con@bind:get
sintassi. GrandchildMessage
Aggiornamenti quandoChildMessageChanged
viene eseguito con@bind:set
la sintassi.
Shared/NestedChild.razor
:
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<NestedGrandchild @bind-GrandchildMessage:get="ChildMessage"
@bind-GrandchildMessage:set="ChildMessageChanged" />
</div>
@code {
[Parameter]
public string? ChildMessage { get; set; }
[Parameter]
public EventCallback<string> ChildMessageChanged { get; set; }
private async Task ChangeValue()
{
await ChildMessageChanged.InvokeAsync(
$"Set in Child {DateTime.Now}");
}
}
Shared/NestedGrandchild.razor
:
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string? GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue()
{
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
}
Per un approccio alternativo adatto alla condivisione dei dati in memoria e tra i componenti che non sono necessariamente annidati, vedere ASP.NET Core Blazor gestione dello stato.
Risorse aggiuntive
- Rilevamento delle modifiche dei parametri e indicazioni aggiuntive sul Razor rendering dei componenti
- Blazor ASP.NET Core moduli e componenti di input
- Associazione ai pulsanti di opzione in un modulo
- Opzioni di associazione
InputSelect
ai valori degli oggettinull
C# - Blazor ASP.NET Core gestione degli eventi:
EventCallback
sezione - Blazor esempi del repository GitHub (
dotnet/blazor-samples
)
Razor i componenti forniscono funzionalità di data binding con l'attributo @bind
Razor direttiva con un campo, una proprietà o Razor un valore di espressione.
L'esempio seguente associa:
- Valore
<input>
dell'elemento nel campo C#inputValue
. - Un secondo
<input>
valore dell'elemento nella proprietà C#InputValue
.
Quando un <input>
elemento perde lo stato attivo, viene aggiornato il campo o la proprietà associata.
Pages/Bind.razor
:
@page "/bind"
<p>
<input @bind="inputValue" />
</p>
<p>
<input @bind="InputValue" />
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string? inputValue;
private string? InputValue { get; set; }
}
La casella di testo viene aggiornata nell'interfaccia utente solo quando il componente viene eseguito il rendering, non in risposta alla modifica del valore del campo o della proprietà. Poiché i componenti vengono visualizzati dopo l'esecuzione del codice del gestore eventi, gli aggiornamenti dei campi e delle proprietà vengono in genere riflessi nell'interfaccia utente immediatamente dopo l'attivazione di un gestore eventi.
Come dimostrazione del modo in cui il data binding si compone in HTML, nell'esempio seguente viene associata la InputValue
proprietà agli attributi e onchange
agli attributi del value
secondo <input>
elemento (change
). Il secondo <input>
elemento nell'esempio seguente è una dimostrazione di concetto e non è destinato a suggerire come associare i dati nei Razor componenti.
Pages/BindTheory.razor
:
@page "/bind-theory"
<p>
<label>
Normal Blazor binding:
<input @bind="InputValue" />
</label>
</p>
<p>
<label>
Demonstration of equivalent HTML binding:
<input value="@InputValue"
@onchange="@((ChangeEventArgs __e) => InputValue = __e?.Value?.ToString())" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
Quando viene eseguito il rendering del componente, l'elemento BindTheory
value
dimostrativo <input>
HTML proviene dalla InputValue
proprietà . Quando l'utente immette un valore nella casella di testo e modifica lo stato attivo dell'elemento, l'evento onchange
viene attivato e la InputValue
proprietà viene impostata sul valore modificato. In realtà, l'esecuzione del codice è più complessa perché @bind
gestisce i casi in cui vengono eseguite le conversioni dei tipi. In generale, @bind
associa il valore corrente di un'espressione all'attributo dell'oggetto value
<input>
e gestisce le modifiche usando il gestore registrato.
Associare una proprietà o un campo ad altri eventi DOM (Document Object Model) includendo un attributo con un @bind:event="{EVENT}"
evento DOM per il {EVENT}
segnaposto. Nell'esempio seguente la InputValue
proprietà viene associata al <input>
valore dell'elemento oninput
quando viene attivato l'evento dell'elemento (input
). A differenza dell'evento (change
), che viene generato quando l'elemento onchange
perde lo stato attivo, oninput
(input
) viene generato quando il valore della casella di testo cambia.
Page/BindEvent.razor
:
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
Razor l'associazione di attributi è distinzione tra maiuscole e minuscole:
@bind
e@bind:event
sono validi.@Bind
/@Bind:Event
(lettere maiuscole eE
) o@BIND:EVENT
@BIND
/(tutte le lettereB
maiuscole) non sono valide.
Selezione di più opzioni con <select>
elementi
L'associazione supporta la multiple
selezione delle opzioni con <select>
elementi. L'evento @onchange
fornisce una matrice degli elementi selezionati tramite argomenti evento (ChangeEventArgs
). Il valore deve essere associato a un tipo di matrice.
Pages/BindMultipleInput.razor
:
@page "/bind-multiple-input"
<h1>Bind Multiple <code>input</code>Example</h1>
<p>
<label>
Select one or more cars:
<select @onchange="SelectedCarsChanged" multiple>
<option value="audi">Audi</option>
<option value="jeep">Jeep</option>
<option value="opel">Opel</option>
<option value="saab">Saab</option>
<option value="volvo">Volvo</option>
</select>
</label>
</p>
<p>
Selected Cars: @string.Join(", ", SelectedCars)
</p>
<p>
<label>
Select one or more cities:
<select @bind="SelectedCities" multiple>
<option value="bal">Baltimore</option>
<option value="la">Los Angeles</option>
<option value="pdx">Portland</option>
<option value="sf">San Francisco</option>
<option value="sea">Seattle</option>
</select>
</label>
</p>
<span>
Selected Cities: @string.Join(", ", SelectedCities)
</span>
@code {
public string[] SelectedCars { get; set; } = new string[] { };
public string[] SelectedCities { get; set; } = new[] { "bal", "sea" };
private void SelectedCarsChanged(ChangeEventArgs e)
{
if (e.Value is not null)
{
SelectedCars = (string[])e.Value;
}
}
}
Per informazioni sul modo in cui vengono gestite stringhe e null
valori vuoti nel data binding, vedere la sezione Opzioni dell'elemento Binding <select>
ai valori degli oggetti null
C# .
Opzioni dell'elemento di associazione <select>
ai valori degli oggetti null
C#
Non è possibile rappresentare un <select>
valore dell'opzione di elemento come valore dell'oggetto null
C# perché:
- Gli attributi HTML non possono avere
null
valori. L'equivalente più vicino anull
in HTML è assenza dell'attributo HTMLvalue
dall'elemento<option>
. - Quando si seleziona un oggetto
<option>
senzavalue
attributo, il browser considera il valore come contenuto di testo di tale<option>
elemento.
Il Blazor framework non tenta di eliminare il comportamento predefinito perché implica:
- Creazione di una catena di soluzioni alternative speciali nel framework.
- Modifiche di rilievo al comportamento corrente del framework.
L'equivalente più plausibile null
in HTML è una stringavalue
vuota. Il Blazor framework gestisce null
le conversioni di stringhe vuote per l'associazione bidirezionale al valore di un <select>
oggetto .
Valori non riparabili
Quando un utente fornisce un valore non verificabile a un elemento in ingresso dati, il valore non verificabile viene automaticamente ripristinato al relativo valore precedente quando viene attivato l'evento di associazione.
Prendere in considerazione il componente seguente, in cui un <input>
elemento è associato a un tipo con un int
valore iniziale di 123
.
Pages/UnparsableValues.razor
:
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
Per impostazione predefinita, l'associazione si applica all'evento dell'elemento onchange
. Se l'utente aggiorna il valore della voce della casella di testo in 123.45
e modifica lo stato attivo, il valore dell'elemento viene ripristinato 123
quando onchange
viene generato. Quando il valore viene rifiutato a favore del valore 123.45
originale di 123
, l'utente riconosce che il valore non è stato accettato.
Per l'evento oninput
(@bind:event="oninput"
), si verifica una riversione del valore dopo qualsiasi sequenza di tasti che introduce un valore non verificabile. Quando si punta all'evento oninput
con un tipo associato, un utente non può digitare un int
carattere punto (.
). Un carattere dot (.
) viene rimosso immediatamente, quindi l'utente riceve un feedback immediato che solo i numeri interi sono consentiti. Esistono scenari in cui il ripristino del valore dell'evento oninput
non è ideale, ad esempio quando l'utente deve essere autorizzato a cancellare un valore non verificabile <input>
. Le alternative includono:
- Non usare l'evento
oninput
. Usare l'evento predefinitoonchange
, in cui un valore non valido non viene ripristinato finché l'elemento non perde lo stato attivo. - Eseguire il binding a un tipo nullable, ad esempio
int?
ostring
e fornire l'associazione a una proprietà con logica personalizzataget
eset
di accesso per gestire voci non valide. - Usare un componente di convalida del modulo, ad InputNumber<TValue> esempio o InputDate<TValue>. I componenti di convalida dei moduli forniscono il supporto predefinito per gestire gli input non validi. Componenti di convalida del modulo:
- Consentire all'utente di specificare errori di input non validi e ricevere errori di convalida nell'oggetto associato EditContext.
- Visualizzare gli errori di convalida nell'interfaccia utente senza interferire con l'utente immettendo dati webform aggiuntivi.
Stringhe di formato
Il data binding funziona con una singola DateTime stringa di formato usando @bind:format="{FORMAT STRING}"
, dove il segnaposto è la {FORMAT STRING}
stringa di formato. Altre espressioni di formato, ad esempio i formati valuta o numero, non sono disponibili in questo momento, ma potrebbero essere aggiunte in una versione futura.
Pages/DateBinding.razor
:
@page "/date-binding"
<p>
<label>
<code>yyyy-MM-dd</code> format:
<input @bind="startDate" @bind:format="yyyy-MM-dd" />
</label>
</p>
<p>
<code>startDate</code>: @startDate
</p>
@code {
private DateTime startDate = new(2020, 1, 1);
}
Nel codice precedente il <input>
tipo di campo dell'elemento (type
attributo) è predefinito su text
.
Nullable System.DateTime e System.DateTimeOffset sono supportati:
private DateTime? date;
private DateTimeOffset? dateOffset;
Se si specifica un formato per il date
tipo di campo, non è consigliabile perché Blazor è disponibile il supporto predefinito per formattare le date. Nonostante la raccomandazione, usare solo il formato data per l'associazione yyyy-MM-dd
per funzionare correttamente se viene fornito un formato con il date
tipo di campo:
<input type="date" @bind="startDate" @bind:format="yyyy-MM-dd">
Associazione a una proprietà con C# get
e set
funzioni di accesso
Le funzioni di accesso e set
C# get
possono essere usate per creare un comportamento di formato di associazione personalizzato, come illustrato nel componente seguenteDecimalBinding
. Il componente associa un decimale positivo o negativo con un massimo di tre cifre decimali a un <input>
elemento tramite una string
proprietà (DecimalValue
).
Pages/DecimalBinding.razor
:
@page "/decimal-binding"
@using System.Globalization
<p>
<label>
Decimal value (±0.000 format):
<input @bind="DecimalValue" />
</label>
</p>
<p>
<code>decimalValue</code>: @decimalValue
</p>
@code {
private decimal decimalValue = 1.1M;
private NumberStyles style =
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string DecimalValue
{
get => decimalValue.ToString("0.000", culture);
set
{
if (Decimal.TryParse(value, style, culture, out var number))
{
decimalValue = Math.Round(number, 3);
}
}
}
}
Associazione con parametri del componente
Uno scenario comune associa una proprietà di un componente figlio a una proprietà nel relativo componente padre. Questo scenario viene chiamato associazione concatenata perché si verificano simultaneamente più livelli di associazione.
I parametri del componente consentono le proprietà di associazione di un componente padre con @bind-{PROPERTY}
sintassi, in cui il {PROPERTY}
segnaposto è la proprietà da associare.
Non è possibile implementare associazioni concatenati con @bind
sintassi nel componente figlio. Un gestore eventi e un valore devono essere specificati separatamente per supportare l'aggiornamento della proprietà nel componente padre dal componente figlio.
Il componente padre sfrutta ancora la @bind
sintassi per configurare l'associazione dei dati con il componente figlio.
Il componente seguente ChildBind
include un Year
parametro componente e un EventCallback<TValue>oggetto . Per convenzione, il EventCallback<TValue> parametro per il parametro deve essere denominato come nome del parametro del componente con un suffisso "Changed
". La sintassi di denominazione è , dove il segnaposto è {PARAMETER NAME}Changed
il {PARAMETER NAME}
nome del parametro. Nell'esempio seguente viene EventCallback<TValue> denominato YearChanged
.
EventCallback.InvokeAsync richiama il delegato associato all'associazione con l'argomento specificato e invia una notifica evento per la proprietà modificata.
Shared/ChildBind.razor
:
<div class="card bg-light mt-3" style="width:18rem ">
<div class="card-body">
<h3 class="card-title">ChildBind Component</h3>
<p class="card-text">
Child <code>Year</code>: @Year
</p>
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
</div>
</div>
@code {
private Random r = new();
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild()
{
await YearChanged.InvokeAsync(r.Next(1950, 2021));
}
}
Per altre informazioni sugli eventi e EventCallback<TValue>, vedere la sezione EventCallback dell'articolo sulla gestione degli eventi ASP.NET CoreBlazor.
Nel componente seguente Parent1
il year
campo è associato al Year
parametro del componente figlio. Il Year
parametro è associabile perché ha un evento complementare YearChanged
che corrisponde al tipo del Year
parametro.
Pages/Parent1.razor
:
@page "/parent-1"
<h1>Parent Component</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private Random r = new();
private int year = 1979;
private void UpdateYear()
{
year = r.Next(1950, 2021);
}
}
Per convenzione, una proprietà può essere associata a un gestore eventi corrispondente includendo un @bind-{PROPERTY}:event
attributo assegnato al gestore, dove il {PROPERTY}
segnaposto è la proprietà. <ChildBind @bind-Year="year" />
equivale alla scrittura:
<ChildBind @bind-Year="year" @bind-Year:event="YearChanged" />
In un esempio più sofisticato e reale, il componente seguente PasswordEntry
:
- Imposta il valore di un
<input>
elemento su unpassword
campo. - Espone le modifiche di una proprietà a un
Password
componente padre con unEventCallback
oggetto che passa il valore corrente del campo figliopassword
come argomento. - Usa l'evento per attivare il
onclick
ToggleShowPassword
metodo. Per altre informazioni, vedere gestione degli eventi ASP.NET CoreBlazor.
Shared/PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
Il PasswordEntry
componente viene usato in un altro componente, ad esempio l'esempio di componente seguente PasswordBinding
.
Pages/PasswordBinding.razor
:
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
Quando il componente viene inizialmente eseguito il PasswordBinding
rendering, il password
valore di Not set
viene visualizzato nell'interfaccia utente. Dopo il rendering iniziale, il valore di password
riflette le modifiche apportate al Password
valore del parametro del componente nel PasswordEntry
componente.
Nota
L'esempio precedente associa la password unidirezionale dal componente figlio PasswordEntry
al componente padre PasswordBinding
. L'associazione bidirezionale non è un requisito in questo scenario se l'obiettivo è che l'app disponga di un componente di immissione della password condivisa per il riutilizzo dell'app che passa semplicemente la password all'elemento padre. Per un approccio che consente l'associazione bidirezionale senza scrivere direttamente nel parametro del componente figlio, vedere l'esempioNestedChild
del componente nella sezione Bind in più di due componenti di questo articolo.
Eseguire controlli o errori di trappola nel gestore. Il componente modificato PasswordEntry
seguente fornisce commenti e suggerimenti immediati all'utente se viene usato uno spazio nel valore della password.
Shared/PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
private string? validationMessage;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
if (password != null && password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
Associare più di due componenti
È possibile associare i parametri tramite qualsiasi numero di componenti annidati, ma è necessario rispettare il flusso unidirezionale dei dati:
- Le notifiche di modifica passano alla gerarchia.
- I nuovi valori dei parametri passano verso il basso nella gerarchia.
Un approccio comune e consigliato consiste nell'archiviare solo i dati sottostanti nel componente padre per evitare qualsiasi confusione sullo stato da aggiornare, come illustrato nell'esempio seguente.
Pages/Parent2.razor
:
@page "/parent-2"
<h1>Parent Component</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue()
{
parentMessage = $"Set in Parent {DateTime.Now}";
}
}
Shared/NestedChild.razor
:
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<NestedGrandchild @bind-GrandchildMessage="BoundValue" />
</div>
@code {
[Parameter]
public string? ChildMessage { get; set; }
[Parameter]
public EventCallback<string> ChildMessageChanged { get; set; }
private string BoundValue
{
get => ChildMessage ?? string.Empty;
set => ChildMessageChanged.InvokeAsync(value);
}
private async Task ChangeValue()
{
await ChildMessageChanged.InvokeAsync(
$"Set in Child {DateTime.Now}");
}
}
Avviso
In genere, evitare di creare componenti che scrivono direttamente nei propri parametri del componente. Il componente precedente NestedChild
usa una BoundValue
proprietà anziché scrivere direttamente nel parametro ChildMessage
. Per altre informazioni, vedere componenti ASP.NET CoreRazor.
Shared/NestedGrandchild.razor
:
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string? GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue()
{
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
}
Per un approccio alternativo adatto alla condivisione dei dati in memoria e tra i componenti che non sono necessariamente annidati, vedere ASP.NET Core Blazor gestione dello stato.
Risorse aggiuntive
- Rilevamento delle modifiche dei parametri e indicazioni aggiuntive sul Razor rendering dei componenti
- Blazor ASP.NET Core moduli e componenti di input
- Associazione ai pulsanti di opzione in un modulo
- Opzioni di associazione
InputSelect
ai valori degli oggettinull
C# - Blazor ASP.NET Core gestione degli eventi:
EventCallback
sezione - Blazor esempi del repository GitHub (
dotnet/blazor-samples
)
Razor i componenti forniscono funzionalità di data binding con l'attributo @bind
Razor direttiva con un campo, una proprietà o Razor un valore di espressione.
L'esempio seguente associa:
- Valore
<input>
dell'elemento nel campo C#inputValue
. - Un secondo
<input>
valore dell'elemento nella proprietà C#InputValue
.
Quando un <input>
elemento perde lo stato attivo, viene aggiornato il campo o la proprietà associata.
Pages/Bind.razor
:
@page "/bind"
<p>
<input @bind="inputValue" />
</p>
<p>
<input @bind="InputValue" />
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string inputValue;
private string InputValue { get; set; }
}
La casella di testo viene aggiornata nell'interfaccia utente solo quando il componente viene eseguito il rendering, non in risposta alla modifica del valore del campo o della proprietà. Poiché i componenti vengono visualizzati dopo l'esecuzione del codice del gestore eventi, gli aggiornamenti dei campi e delle proprietà vengono in genere riflessi nell'interfaccia utente immediatamente dopo l'attivazione di un gestore eventi.
Come dimostrazione del modo in cui il data binding si compone in HTML, nell'esempio seguente viene associata la InputValue
proprietà agli attributi e onchange
agli attributi del value
secondo <input>
elemento (change
). Il secondo <input>
elemento nell'esempio seguente è una dimostrazione di concetto e non è destinato a suggerire come associare i dati nei Razor componenti.
Pages/BindTheory.razor
:
@page "/bind-theory"
<p>
<label>
Normal Blazor binding:
<input @bind="InputValue" />
</label>
</p>
<p>
<label>
Demonstration of equivalent HTML binding:
<input value="@InputValue"
@onchange="@((ChangeEventArgs __e) => InputValue = __e.Value.ToString())" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string InputValue { get; set; }
}
Quando viene eseguito il rendering del componente, l'elemento BindTheory
value
dimostrativo <input>
HTML proviene dalla InputValue
proprietà . Quando l'utente immette un valore nella casella di testo e modifica lo stato attivo dell'elemento, l'evento onchange
viene attivato e la InputValue
proprietà viene impostata sul valore modificato. In realtà, l'esecuzione del codice è più complessa perché @bind
gestisce i casi in cui vengono eseguite le conversioni dei tipi. In generale, @bind
associa il valore corrente di un'espressione all'attributo dell'oggetto value
<input>
e gestisce le modifiche usando il gestore registrato.
Associare una proprietà o un campo ad altri eventi DOM (Document Object Model) includendo un attributo con un @bind:event="{EVENT}"
evento DOM per il {EVENT}
segnaposto. Nell'esempio seguente la InputValue
proprietà viene associata al <input>
valore dell'elemento oninput
quando viene attivato l'evento dell'elemento (input
). A differenza dell'evento (change
), che viene generato quando l'elemento onchange
perde lo stato attivo, oninput
(input
) viene generato quando il valore della casella di testo cambia.
Page/BindEvent.razor
:
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string InputValue { get; set; }
}
Razor l'associazione di attributi è distinzione tra maiuscole e minuscole:
@bind
e@bind:event
sono validi.@Bind
/@Bind:Event
(lettere maiuscole eE
) o@BIND:EVENT
@BIND
/(tutte le lettereB
maiuscole) non sono valide.
Opzioni dell'elemento di associazione <select>
ai valori degli oggetti null
C#
Non è possibile rappresentare un <select>
valore dell'opzione di elemento come valore dell'oggetto null
C# perché:
- Gli attributi HTML non possono avere
null
valori. L'equivalente più vicino anull
in HTML è assenza dell'attributo HTMLvalue
dall'elemento<option>
. - Quando si seleziona un oggetto
<option>
senzavalue
attributo, il browser considera il valore come contenuto di testo di tale<option>
elemento.
Il Blazor framework non tenta di eliminare il comportamento predefinito perché implica:
- Creazione di una catena di soluzioni alternative speciali nel framework.
- Modifiche di rilievo al comportamento corrente del framework.
L'equivalente più plausibile null
in HTML è una stringavalue
vuota. Il Blazor framework gestisce null
le conversioni di stringhe vuote per l'associazione bidirezionale al valore di un <select>
oggetto .
Valori non riparabili
Quando un utente fornisce un valore non verificabile a un elemento in ingresso dati, il valore non verificabile viene automaticamente ripristinato al relativo valore precedente quando viene attivato l'evento di associazione.
Prendere in considerazione il componente seguente, in cui un <input>
elemento è associato a un tipo con un int
valore iniziale di 123
.
Pages/UnparsableValues.razor
:
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
Per impostazione predefinita, l'associazione si applica all'evento dell'elemento onchange
. Se l'utente aggiorna il valore della voce della casella di testo in 123.45
e modifica lo stato attivo, il valore dell'elemento viene ripristinato 123
quando onchange
viene generato. Quando il valore viene rifiutato a favore del valore 123.45
originale di 123
, l'utente riconosce che il valore non è stato accettato.
Per l'evento oninput
(@bind:event="oninput"
), si verifica una riversione del valore dopo qualsiasi sequenza di tasti che introduce un valore non verificabile. Quando si punta all'evento oninput
con un tipo associato, un utente non può digitare un int
carattere punto (.
). Un carattere dot (.
) viene rimosso immediatamente, quindi l'utente riceve un feedback immediato che solo i numeri interi sono consentiti. Esistono scenari in cui il ripristino del valore dell'evento oninput
non è ideale, ad esempio quando l'utente deve essere autorizzato a cancellare un valore non verificabile <input>
. Le alternative includono:
- Non usare l'evento
oninput
. Usare l'evento predefinitoonchange
, in cui un valore non valido non viene ripristinato finché l'elemento non perde lo stato attivo. - Eseguire il binding a un tipo nullable, ad esempio
int?
ostring
e fornire l'associazione a una proprietà con logica personalizzataget
eset
di accesso per gestire voci non valide. - Usare un componente di convalida del modulo, ad InputNumber<TValue> esempio o InputDate<TValue>. I componenti di convalida dei moduli forniscono il supporto predefinito per gestire gli input non validi. Componenti di convalida del modulo:
- Consentire all'utente di specificare errori di input non validi e ricevere errori di convalida nell'oggetto associato EditContext.
- Visualizzare gli errori di convalida nell'interfaccia utente senza interferire con l'utente immettendo dati webform aggiuntivi.
Stringhe di formato
Il data binding funziona con una singola DateTime stringa di formato usando @bind:format="{FORMAT STRING}"
, dove il segnaposto è la {FORMAT STRING}
stringa di formato. Altre espressioni di formato, ad esempio i formati valuta o numero, non sono disponibili in questo momento, ma potrebbero essere aggiunte in una versione futura.
Pages/DateBinding.razor
:
@page "/date-binding"
<p>
<label>
<code>yyyy-MM-dd</code> format:
<input @bind="startDate" @bind:format="yyyy-MM-dd" />
</label>
</p>
<p>
<code>startDate</code>: @startDate
</p>
@code {
private DateTime startDate = new(2020, 1, 1);
}
Nel codice precedente il <input>
tipo di campo dell'elemento (type
attributo) è predefinito su text
.
Nullable System.DateTime e System.DateTimeOffset sono supportati:
private DateTime? date;
private DateTimeOffset? dateOffset;
Se si specifica un formato per il date
tipo di campo, non è consigliabile perché Blazor è disponibile il supporto predefinito per formattare le date. Nonostante la raccomandazione, usare solo il formato data per l'associazione yyyy-MM-dd
per funzionare correttamente se viene fornito un formato con il date
tipo di campo:
<input type="date" @bind="startDate" @bind:format="yyyy-MM-dd">
Associazione a una proprietà con C# get
e set
funzioni di accesso
Le funzioni di accesso e set
C# get
possono essere usate per creare un comportamento di formato di associazione personalizzato, come illustrato nel componente seguenteDecimalBinding
. Il componente associa un decimale positivo o negativo con un massimo di tre cifre decimali a un <input>
elemento tramite una string
proprietà (DecimalValue
).
Pages/DecimalBinding.razor
:
@page "/decimal-binding"
@using System.Globalization
<p>
<label>
Decimal value (±0.000 format):
<input @bind="DecimalValue" />
</label>
</p>
<p>
<code>decimalValue</code>: @decimalValue
</p>
@code {
private decimal decimalValue = 1.1M;
private NumberStyles style =
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string DecimalValue
{
get => decimalValue.ToString("0.000", culture);
set
{
if (Decimal.TryParse(value, style, culture, out var number))
{
decimalValue = Math.Round(number, 3);
}
}
}
}
Associazione con parametri del componente
Uno scenario comune associa una proprietà di un componente figlio a una proprietà nel relativo componente padre. Questo scenario viene chiamato associazione concatenata perché si verificano simultaneamente più livelli di associazione.
I parametri del componente consentono le proprietà di associazione di un componente padre con @bind-{PROPERTY}
sintassi, in cui il {PROPERTY}
segnaposto è la proprietà da associare.
Non è possibile implementare associazioni concatenati con @bind
sintassi nel componente figlio. Un gestore eventi e un valore devono essere specificati separatamente per supportare l'aggiornamento della proprietà nel componente padre dal componente figlio.
Il componente padre sfrutta ancora la @bind
sintassi per configurare l'associazione dei dati con il componente figlio.
Il componente seguente ChildBind
include un Year
parametro componente e un EventCallback<TValue>oggetto . Per convenzione, il EventCallback<TValue> parametro per il parametro deve essere denominato come nome del parametro del componente con un suffisso "Changed
". La sintassi di denominazione è , dove il segnaposto è {PARAMETER NAME}Changed
il {PARAMETER NAME}
nome del parametro. Nell'esempio seguente viene EventCallback<TValue> denominato YearChanged
.
EventCallback.InvokeAsync richiama il delegato associato all'associazione con l'argomento specificato e invia una notifica evento per la proprietà modificata.
Shared/ChildBind.razor
:
<div class="card bg-light mt-3" style="width:18rem ">
<div class="card-body">
<h3 class="card-title">ChildBind Component</h3>
<p class="card-text">
Child <code>Year</code>: @Year
</p>
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
</div>
</div>
@code {
private Random r = new();
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild()
{
await YearChanged.InvokeAsync(r.Next(1950, 2021));
}
}
Per altre informazioni sugli eventi e EventCallback<TValue>, vedere la sezione EventCallback dell'articolo sulla gestione degli eventi ASP.NET CoreBlazor.
Nel componente seguente Parent1
il year
campo è associato al Year
parametro del componente figlio. Il Year
parametro è associabile perché ha un evento complementare YearChanged
che corrisponde al tipo del Year
parametro.
Pages/Parent1.razor
:
@page "/parent-1"
<h1>Parent Component</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private Random r = new();
private int year = 1979;
private void UpdateYear()
{
year = r.Next(1950, 2021);
}
}
Per convenzione, una proprietà può essere associata a un gestore eventi corrispondente includendo un @bind-{PROPERTY}:event
attributo assegnato al gestore, dove il {PROPERTY}
segnaposto è la proprietà. <ChildBind @bind-Year="year" />
equivale alla scrittura:
<ChildBind @bind-Year="year" @bind-Year:event="YearChanged" />
In un esempio più sofisticato e reale, il componente seguente PasswordEntry
:
- Imposta il valore di un
<input>
elemento su unpassword
campo. - Espone le modifiche di una proprietà a un
Password
componente padre con unEventCallback
oggetto che passa il valore corrente del campo figliopassword
come argomento. - Usa l'evento per attivare il
onclick
ToggleShowPassword
metodo. Per altre informazioni, vedere gestione degli eventi ASP.NET CoreBlazor.
Shared/PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
Il PasswordEntry
componente viene usato in un altro componente, ad esempio l'esempio di componente seguente PasswordBinding
.
Pages/PasswordBinding.razor
:
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
Quando il componente viene inizialmente eseguito il PasswordBinding
rendering, il password
valore di Not set
viene visualizzato nell'interfaccia utente. Dopo il rendering iniziale, il valore di password
riflette le modifiche apportate al Password
valore del parametro del componente nel PasswordEntry
componente.
Nota
L'esempio precedente associa la password unidirezionale dal componente figlio PasswordEntry
al componente padre PasswordBinding
. L'associazione bidirezionale non è un requisito in questo scenario se l'obiettivo è che l'app disponga di un componente di immissione della password condivisa per il riutilizzo dell'app che passa semplicemente la password all'elemento padre. Per un approccio che consente l'associazione bidirezionale senza scrivere direttamente nel parametro del componente figlio, vedere l'esempioNestedChild
del componente nella sezione Bind in più di due componenti di questo articolo.
Eseguire controlli o errori di trappola nel gestore. Il componente modificato PasswordEntry
seguente fornisce commenti e suggerimenti immediati all'utente se viene usato uno spazio nel valore della password.
Shared/PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
private string validationMessage;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
if (password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
Associare più di due componenti
È possibile associare i parametri tramite qualsiasi numero di componenti annidati, ma è necessario rispettare il flusso unidirezionale dei dati:
- Le notifiche di modifica passano alla gerarchia.
- I nuovi valori dei parametri passano verso il basso nella gerarchia.
Un approccio comune e consigliato consiste nell'archiviare solo i dati sottostanti nel componente padre per evitare qualsiasi confusione sullo stato da aggiornare, come illustrato nell'esempio seguente.
Pages/Parent2.razor
:
@page "/parent-2"
<h1>Parent Component</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue()
{
parentMessage = $"Set in Parent {DateTime.Now}";
}
}
Shared/NestedChild.razor
:
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<NestedGrandchild @bind-GrandchildMessage="BoundValue" />
</div>
@code {
[Parameter]
public string ChildMessage { get; set; }
[Parameter]
public EventCallback<string> ChildMessageChanged { get; set; }
private string BoundValue
{
get => ChildMessage;
set => ChildMessageChanged.InvokeAsync(value);
}
private async Task ChangeValue()
{
await ChildMessageChanged.InvokeAsync(
$"Set in Child {DateTime.Now}");
}
}
Avviso
In genere, evitare di creare componenti che scrivono direttamente nei propri parametri del componente. Il componente precedente NestedChild
usa una BoundValue
proprietà anziché scrivere direttamente nel parametro ChildMessage
. Per altre informazioni, vedere componenti ASP.NET CoreRazor.
Shared/NestedGrandchild.razor
:
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue()
{
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
}
Per un approccio alternativo adatto alla condivisione dei dati in memoria e tra i componenti che non sono necessariamente annidati, vedere ASP.NET Core Blazor gestione dello stato.
Risorse aggiuntive
- Rilevamento delle modifiche dei parametri e indicazioni aggiuntive sul Razor rendering dei componenti
- Blazor ASP.NET Core moduli e componenti di input
- Associazione ai pulsanti di opzione in un modulo
- Opzioni di associazione
InputSelect
ai valori degli oggettinull
C# - Blazor ASP.NET Core gestione degli eventi:
EventCallback
sezione - Blazor esempi del repository GitHub (
dotnet/blazor-samples
)
Razor i componenti forniscono funzionalità di data binding con l'attributo @bind
Razor direttiva con un campo, una proprietà o Razor un valore di espressione.
L'esempio seguente associa:
- Valore
<input>
dell'elemento nel campo C#inputValue
. - Un secondo
<input>
valore dell'elemento nella proprietà C#InputValue
.
Quando un <input>
elemento perde lo stato attivo, viene aggiornato il campo o la proprietà associata.
Pages/Bind.razor
:
@page "/bind"
<p>
<input @bind="inputValue" />
</p>
<p>
<input @bind="InputValue" />
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string inputValue;
private string InputValue { get; set; }
}
La casella di testo viene aggiornata nell'interfaccia utente solo quando il componente viene eseguito il rendering, non in risposta alla modifica del valore del campo o della proprietà. Poiché i componenti vengono visualizzati dopo l'esecuzione del codice del gestore eventi, gli aggiornamenti dei campi e delle proprietà vengono in genere riflessi nell'interfaccia utente immediatamente dopo l'attivazione di un gestore eventi.
Come dimostrazione del modo in cui il data binding si compone in HTML, nell'esempio seguente viene associata la InputValue
proprietà agli attributi e onchange
agli attributi del value
secondo <input>
elemento (change
). Il secondo <input>
elemento nell'esempio seguente è una dimostrazione di concetto e non è destinato a suggerire come associare i dati nei Razor componenti.
Pages/BindTheory.razor
:
@page "/bind-theory"
<p>
<label>
Normal Blazor binding:
<input @bind="InputValue" />
</label>
</p>
<p>
<label>
Demonstration of equivalent HTML binding:
<input value="@InputValue"
@onchange="@((ChangeEventArgs __e) => InputValue = __e.Value.ToString())" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string InputValue { get; set; }
}
Quando viene eseguito il rendering del componente, l'elemento BindTheory
value
dimostrativo <input>
HTML proviene dalla InputValue
proprietà . Quando l'utente immette un valore nella casella di testo e modifica lo stato attivo dell'elemento, l'evento onchange
viene attivato e la InputValue
proprietà viene impostata sul valore modificato. In realtà, l'esecuzione del codice è più complessa perché @bind
gestisce i casi in cui vengono eseguite le conversioni dei tipi. In generale, @bind
associa il valore corrente di un'espressione all'attributo dell'oggetto value
<input>
e gestisce le modifiche usando il gestore registrato.
Associare una proprietà o un campo ad altri eventi DOM (Document Object Model) includendo un attributo con un @bind:event="{EVENT}"
evento DOM per il {EVENT}
segnaposto. Nell'esempio seguente la InputValue
proprietà viene associata al <input>
valore dell'elemento oninput
quando viene attivato l'evento dell'elemento (input
). A differenza dell'evento (change
), che viene generato quando l'elemento onchange
perde lo stato attivo, oninput
(input
) viene generato quando il valore della casella di testo cambia.
Page/BindEvent.razor
:
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string InputValue { get; set; }
}
Razor l'associazione di attributi è distinzione tra maiuscole e minuscole:
@bind
e@bind:event
sono validi.@Bind
/@Bind:Event
(lettere maiuscole eE
) o@BIND:EVENT
@BIND
/(tutte le lettereB
maiuscole) non sono valide.
Opzioni dell'elemento di associazione <select>
ai valori degli oggetti null
C#
Non è possibile rappresentare un <select>
valore dell'opzione di elemento come valore dell'oggetto null
C# perché:
- Gli attributi HTML non possono avere
null
valori. L'equivalente più vicino anull
in HTML è assenza dell'attributo HTMLvalue
dall'elemento<option>
. - Quando si seleziona un oggetto
<option>
senzavalue
attributo, il browser considera il valore come contenuto di testo di tale<option>
elemento.
Il Blazor framework non tenta di eliminare il comportamento predefinito perché implica:
- Creazione di una catena di soluzioni alternative speciali nel framework.
- Modifiche di rilievo al comportamento corrente del framework.
Il Blazor framework non gestisce null
automaticamente le conversioni di stringhe vuote durante il tentativo di associazione bidirezionale al valore di un'istanza <select>
. Per altre informazioni, vedere Correzione dell'associazione <select>
a un valore Null (dotnet/aspnetcore #23221).
Valori non riparabili
Quando un utente fornisce un valore non verificabile a un elemento in ingresso dati, il valore non verificabile viene automaticamente ripristinato al relativo valore precedente quando viene attivato l'evento di associazione.
Prendere in considerazione il componente seguente, in cui un <input>
elemento è associato a un tipo con un int
valore iniziale di 123
.
Pages/UnparsableValues.razor
:
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
Per impostazione predefinita, l'associazione si applica all'evento dell'elemento onchange
. Se l'utente aggiorna il valore della voce della casella di testo in 123.45
e modifica lo stato attivo, il valore dell'elemento viene ripristinato 123
quando onchange
viene generato. Quando il valore viene rifiutato a favore del valore 123.45
originale di 123
, l'utente riconosce che il valore non è stato accettato.
Per l'evento oninput
(@bind:event="oninput"
), si verifica una riversione del valore dopo qualsiasi sequenza di tasti che introduce un valore non verificabile. Quando si punta all'evento oninput
con un tipo associato, un utente non può digitare un int
carattere punto (.
). Un carattere dot (.
) viene rimosso immediatamente, quindi l'utente riceve un feedback immediato che solo i numeri interi sono consentiti. Esistono scenari in cui il ripristino del valore dell'evento oninput
non è ideale, ad esempio quando l'utente deve essere autorizzato a cancellare un valore non verificabile <input>
. Le alternative includono:
- Non usare l'evento
oninput
. Usare l'evento predefinitoonchange
, in cui un valore non valido non viene ripristinato finché l'elemento non perde lo stato attivo. - Eseguire il binding a un tipo nullable, ad esempio
int?
ostring
e fornire l'associazione a una proprietà con logica personalizzataget
eset
di accesso per gestire voci non valide. - Usare un componente di convalida del modulo, ad InputNumber<TValue> esempio o InputDate<TValue>. I componenti di convalida dei moduli forniscono il supporto predefinito per gestire gli input non validi. Componenti di convalida del modulo:
- Consentire all'utente di specificare errori di input non validi e ricevere errori di convalida nell'oggetto associato EditContext.
- Visualizzare gli errori di convalida nell'interfaccia utente senza interferire con l'utente immettendo dati webform aggiuntivi.
Stringhe di formato
Il data binding funziona con una singola DateTime stringa di formato usando @bind:format="{FORMAT STRING}"
, dove il segnaposto è la {FORMAT STRING}
stringa di formato. Altre espressioni di formato, ad esempio i formati valuta o numero, non sono disponibili in questo momento, ma potrebbero essere aggiunte in una versione futura.
Pages/DateBinding.razor
:
@page "/date-binding"
<p>
<label>
<code>yyyy-MM-dd</code> format:
<input @bind="startDate" @bind:format="yyyy-MM-dd" />
</label>
</p>
<p>
<code>startDate</code>: @startDate
</p>
@code {
private DateTime startDate = new DateTime(2020, 1, 1);
}
Nel codice precedente il <input>
tipo di campo dell'elemento (type
attributo) è predefinito su text
.
Nullable System.DateTime e System.DateTimeOffset sono supportati:
private DateTime? date;
private DateTimeOffset? dateOffset;
Se si specifica un formato per il date
tipo di campo, non è consigliabile perché Blazor è disponibile il supporto predefinito per formattare le date. Nonostante la raccomandazione, usare solo il formato data per l'associazione yyyy-MM-dd
per funzionare correttamente se viene fornito un formato con il date
tipo di campo:
<input type="date" @bind="startDate" @bind:format="yyyy-MM-dd">
Associazione a una proprietà con C# get
e set
funzioni di accesso
Le funzioni di accesso e set
C# get
possono essere usate per creare un comportamento di formato di associazione personalizzato, come illustrato nel componente seguenteDecimalBinding
. Il componente associa un decimale positivo o negativo con un massimo di tre cifre decimali a un <input>
elemento tramite una string
proprietà (DecimalValue
).
Pages/DecimalBinding.razor
:
@page "/decimal-binding"
@using System.Globalization
<p>
<label>
Decimal value (±0.000 format):
<input @bind="DecimalValue" />
</label>
</p>
<p>
<code>decimalValue</code>: @decimalValue
</p>
@code {
private decimal decimalValue = 1.1M;
private NumberStyles style =
NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string DecimalValue
{
get => decimalValue.ToString("0.000", culture);
set
{
if (Decimal.TryParse(value, style, culture, out var number))
{
decimalValue = Math.Round(number, 3);
}
}
}
}
Associazione con parametri del componente
Uno scenario comune associa una proprietà di un componente figlio a una proprietà nel relativo componente padre. Questo scenario viene chiamato associazione concatenata perché si verificano simultaneamente più livelli di associazione.
I parametri del componente consentono le proprietà di associazione di un componente padre con @bind-{PROPERTY}
sintassi, in cui il {PROPERTY}
segnaposto è la proprietà da associare.
Non è possibile implementare associazioni concatenati con @bind
sintassi nel componente figlio. Un gestore eventi e un valore devono essere specificati separatamente per supportare l'aggiornamento della proprietà nel componente padre dal componente figlio.
Il componente padre sfrutta ancora la @bind
sintassi per configurare l'associazione dei dati con il componente figlio.
Il componente seguente ChildBind
include un Year
parametro componente e un EventCallback<TValue>oggetto . Per convenzione, il EventCallback<TValue> parametro per il parametro deve essere denominato come nome del parametro del componente con un suffisso "Changed
". La sintassi di denominazione è , dove il segnaposto è {PARAMETER NAME}Changed
il {PARAMETER NAME}
nome del parametro. Nell'esempio seguente viene EventCallback<TValue> denominato YearChanged
.
EventCallback.InvokeAsync richiama il delegato associato all'associazione con l'argomento specificato e invia una notifica evento per la proprietà modificata.
Shared/ChildBind.razor
:
<div class="card bg-light mt-3" style="width:18rem ">
<div class="card-body">
<h3 class="card-title">ChildBind Component</h3>
<p class="card-text">
Child <code>Year</code>: @Year
</p>
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
</div>
</div>
@code {
private Random r = new Random();
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild()
{
await YearChanged.InvokeAsync(r.Next(1950, 2021));
}
}
Per altre informazioni sugli eventi e EventCallback<TValue>, vedere la sezione EventCallback dell'articolo sulla gestione degli eventi ASP.NET CoreBlazor.
Nel componente seguente Parent1
il year
campo è associato al Year
parametro del componente figlio. Il Year
parametro è associabile perché ha un evento complementare YearChanged
che corrisponde al tipo del Year
parametro.
Pages/Parent1.razor
:
@page "/parent-1"
<h1>Parent Component</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private Random r = new Random();
private int year = 1979;
private void UpdateYear()
{
year = r.Next(1950, 2021);
}
}
Per convenzione, una proprietà può essere associata a un gestore eventi corrispondente includendo un @bind-{PROPERTY}:event
attributo assegnato al gestore, dove il {PROPERTY}
segnaposto è la proprietà. <ChildBind @bind-Year="year" />
equivale alla scrittura:
<ChildBind @bind-Year="year" @bind-Year:event="YearChanged" />
In un esempio più sofisticato e reale, il componente seguente PasswordEntry
:
- Imposta il valore di un
<input>
elemento su unpassword
campo. - Espone le modifiche di una proprietà a un
Password
componente padre con unEventCallback
oggetto che passa il valore corrente del campo figliopassword
come argomento. - Usa l'evento per attivare il
onclick
ToggleShowPassword
metodo. Per altre informazioni, vedere gestione degli eventi ASP.NET CoreBlazor.
Shared/PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
Il PasswordEntry
componente viene usato in un altro componente, ad esempio l'esempio di componente seguente PasswordBinding
.
Pages/PasswordBinding.razor
:
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
Quando il componente viene inizialmente eseguito il PasswordBinding
rendering, il password
valore di Not set
viene visualizzato nell'interfaccia utente. Dopo il rendering iniziale, il valore di password
riflette le modifiche apportate al Password
valore del parametro del componente nel PasswordEntry
componente.
Nota
L'esempio precedente associa la password unidirezionale dal componente figlio PasswordEntry
al componente padre PasswordBinding
. L'associazione bidirezionale non è un requisito in questo scenario se l'obiettivo è che l'app disponga di un componente di immissione della password condivisa per il riutilizzo dell'app che passa semplicemente la password all'elemento padre. Per un approccio che consente l'associazione bidirezionale senza scrivere direttamente nel parametro del componente figlio, vedere l'esempioNestedChild
del componente nella sezione Bind in più di due componenti di questo articolo.
Eseguire controlli o errori di trappola nel gestore. Il componente modificato PasswordEntry
seguente fornisce commenti e suggerimenti immediati all'utente se viene usato uno spazio nel valore della password.
Shared/PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
private string validationMessage;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
if (password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
Associare più di due componenti
È possibile associare i parametri tramite qualsiasi numero di componenti annidati, ma è necessario rispettare il flusso unidirezionale dei dati:
- Le notifiche di modifica passano alla gerarchia.
- I nuovi valori dei parametri passano verso il basso nella gerarchia.
Un approccio comune e consigliato consiste nell'archiviare solo i dati sottostanti nel componente padre per evitare qualsiasi confusione sullo stato da aggiornare, come illustrato nell'esempio seguente.
Pages/Parent2.razor
:
@page "/parent-2"
<h1>Parent Component</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Parent</button>
</p>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue()
{
parentMessage = $"Set in Parent {DateTime.Now}";
}
}
Shared/NestedChild.razor
:
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Child</button>
</p>
<NestedGrandchild @bind-GrandchildMessage="BoundValue" />
</div>
@code {
[Parameter]
public string ChildMessage { get; set; }
[Parameter]
public EventCallback<string> ChildMessageChanged { get; set; }
private string BoundValue
{
get => ChildMessage;
set => ChildMessageChanged.InvokeAsync(value);
}
private async Task ChangeValue()
{
await ChildMessageChanged.InvokeAsync(
$"Set in Child {DateTime.Now}");
}
}
Avviso
In genere, evitare di creare componenti che scrivono direttamente nei propri parametri del componente. Il componente precedente NestedChild
usa una BoundValue
proprietà anziché scrivere direttamente nel parametro ChildMessage
. Per altre informazioni, vedere componenti ASP.NET CoreRazor.
Shared/NestedGrandchild.razor
:
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<p>
<button @onclick="ChangeValue">Change from Grandchild</button>
</p>
</div>
@code {
[Parameter]
public string GrandchildMessage { get; set; }
[Parameter]
public EventCallback<string> GrandchildMessageChanged { get; set; }
private async Task ChangeValue()
{
await GrandchildMessageChanged.InvokeAsync(
$"Set in Grandchild {DateTime.Now}");
}
}
Per un approccio alternativo adatto alla condivisione dei dati in memoria e tra i componenti che non sono necessariamente annidati, vedere ASP.NET Core Blazor gestione dello stato.
Risorse aggiuntive
- Rilevamento delle modifiche dei parametri e indicazioni aggiuntive sul Razor rendering dei componenti
- Blazor ASP.NET Core moduli e componenti di input
- Associazione ai pulsanti di opzione in un modulo
- Opzioni di associazione
InputSelect
ai valori degli oggettinull
C# - Blazor ASP.NET Core gestione degli eventi:
EventCallback
sezione - Blazor esempi del repository GitHub (
dotnet/blazor-samples
)