Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Catatan
Ini bukan versi terbaru dari artikel ini. Untuk rilis saat ini, lihat versi .NET 10 dari artikel ini.
Peringatan
Versi ASP.NET Core ini tidak lagi didukung. Untuk informasi selengkapnya, lihat Kebijakan Dukungan .NET dan .NET Core. Untuk rilis saat ini, lihat versi .NET 9 dari artikel ini.
Artikel ini menjelaskan fitur pengikatan data untuk Razor komponen dan elemen DOM di Blazor aplikasi.
Fitur pengikatan
Razor komponen menyediakan fitur pengikatan data dengan atribut direktif @bindRazor dengan nilai bidang, properti, atau Razor ekspresi.
Contoh berikut mengikat:
- Nilai
<input>elemen ke bidang C#inputValue. - Nilai elemen kedua
<input>ke properti C#InputValue.
<input> Ketika elemen kehilangan fokus, bidang atau properti terikatnya diperbarui.
Bind.razor:
@page "/bind"
<PageTitle>Bind</PageTitle>
<h1>Bind Example</h1>
<p>
<label>
inputValue:
<input @bind="inputValue" />
</label>
</p>
<p>
<label>
InputValue:
<input @bind="InputValue" />
</label>
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string? inputValue;
private string? InputValue { get; set; }
}
@page "/bind"
<PageTitle>Bind</PageTitle>
<h1>Bind Example</h1>
<p>
<label>
inputValue:
<input @bind="inputValue" />
</label>
</p>
<p>
<label>
InputValue:
<input @bind="InputValue" />
</label>
</p>
<ul>
<li><code>inputValue</code>: @inputValue</li>
<li><code>InputValue</code>: @InputValue</li>
</ul>
@code {
private string? inputValue;
private string? InputValue { get; set; }
}
@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; }
}
@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; }
}
@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; }
}
@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; }
}
Kotak teks diperbarui di UI hanya saat komponen dirender, bukan sebagai respons terhadap perubahan nilai bidang atau properti. Karena komponen merender sendiri setelah kode penanganan aktivitas dijalankan, pembaruan bidang dan properti biasanya tercermin di UI segera setelah penanganan aktivitas dipicu.
Sebagai demonstrasi tentang bagaimana pengikatan data menyusun dalam HTML, contoh berikut mengikat InputValue properti ke elemen <input> dan value atribut kedua onchange (change).
Elemen kedua <input> dalam contoh berikut adalah demonstrasi konsep dan tidak dimaksudkan untuk menyarankan bagaimana Anda harus mengikat data dalam Razor komponen.
BindTheory.razor:
@page "/bind-theory"
<PageTitle>Bind Theory</PageTitle>
<h1>Bind Theory Example</h1>
<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; }
}
@page "/bind-theory"
<PageTitle>Bind Theory</PageTitle>
<h1>Bind Theory Example</h1>
<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; }
}
@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; }
}
@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; }
}
@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; }
}
@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; }
}
BindTheory Ketika komponen dirender, value elemen demonstrasi <input> HTML berasal dari InputValue properti . Saat pengguna memasukkan nilai dalam kotak teks dan mengubah fokus elemen, onchange peristiwa diaktifkan dan InputValue properti diatur ke nilai yang diubah. Pada kenyataannya, eksekusi kode lebih kompleks karena @bind menangani kasus di mana konversi jenis dilakukan. Secara umum, @bind mengaitkan nilai ekspresi saat ini dengan value atribut <input> dan menangani perubahan menggunakan handler terdaftar.
Ikat properti atau bidang pada peristiwa DOM lainnya dengan menyertakan @bind:event="{EVENT}" atribut dengan peristiwa DOM untuk {EVENT} tempat penampung. Contoh berikut mengikat InputValue properti ke <input> nilai elemen saat peristiwa elemen oninput (input) dipicu.
onchange Tidak seperti peristiwa (change), yang diaktifkan saat elemen kehilangan fokus, oninput (input) diaktifkan saat nilai kotak teks berubah.
Page/BindEvent.razor:
@page "/bind-event"
<PageTitle>Bind Event</PageTitle>
<h1>Bind Event Example</h1>
<p>
<label>
InputValue:
<input @bind="InputValue" @bind:event="oninput" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-event"
<PageTitle>Bind Event</PageTitle>
<h1>Bind Event Example</h1>
<p>
<label>
InputValue:
<input @bind="InputValue" @bind:event="oninput" />
</label>
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string? InputValue { get; set; }
}
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string InputValue { get; set; }
}
@page "/bind-event"
<p>
<input @bind="InputValue" @bind:event="oninput" />
</p>
<p>
<code>InputValue</code>: @InputValue
</p>
@code {
private string InputValue { get; set; }
}
Razor pengikatan atribut peka huruf besar/kecil:
-
@binddan@bind:eventvalid. -
@Bind/@Bind:Event(hurufBkapital danE) atau@BIND/@BIND:EVENT(semua huruf kapital) tidak valid.
Untuk menjalankan logika asinkron setelah pengikatan, gunakan @bind:after="{DELEGATE}", di mana {DELEGATE} tempat penampung adalah delegasi C# (metode). Delegasi C# yang ditetapkan tidak dijalankan sampai nilai terikat ditetapkan secara sinkron.
Menggunakan parameter panggilan balik peristiwa (EventCallback/EventCallback<T>) dengan @bind:after tidak didukung. Sebagai gantinya, berikan metode yang mengembalikan Action atau Task ke @bind:after.
Dalam contoh berikut:
- Setiap
<input>elemenvalueterikat kesearchTextbidang secara sinkron. - Metode ini
PerformSearchdijalankan secara asinkron:- Saat kotak pertama kehilangan fokus (
onchangeperistiwa) setelah nilai diubah. - Setelah setiap penekanan tombol (
oninputperistiwa) di kotak kedua.
- Saat kotak pertama kehilangan fokus (
-
PerformSearchmemanggil layanan dengan metode asinkron (FetchAsync) untuk mengembalikan hasil pencarian.
@inject ISearchService SearchService
<input @bind="searchText" @bind:after="PerformSearch" />
<input @bind="searchText" @bind:event="oninput" @bind:after="PerformSearch" />
@code {
private string? searchText;
private string[]? searchResult;
private async Task PerformSearch() =>
searchResult = await SearchService.FetchAsync(searchText);
}
Contoh tambahan
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; }
}
Untuk informasi selengkapnya tentang InputText komponen, lihat Blazor input ASP.NET Core.
Komponen mendukung pengikatan data dua arah dengan menentukan sepasang atribut @bind dengan pengubah :get atau :set . Penanda {PARAMETER} dalam contoh berikut ini digunakan untuk mengikat parameter komponen.
-
@bind:get/@bind-{PARAMETER}:get: Menentukan nilai yang akan diikat. -
@bind:set/@bind-{PARAMETER}:set: Menentukan panggilan balik saat nilai berubah.
Pengubah :get dan :set selalu digunakan bersama-sama.
Dengan pengikatan :get/:set, Anda dapat bereaksi terhadap perubahan nilai sebelum diterapkan ke DOM, dan Anda dapat mengubah nilai yang diterapkan, jika perlu. Sedangkan dengan @bind:event="{EVENT}" sebagai atribut pengikatan, di mana placeholder {EVENT} adalah peristiwa DOM, Anda menerima pemberitahuan setelah DOM diperbarui, dan tidak ada kemampuan untuk memodifikasi nilai yang diterapkan selama proses pengikatan.
Komponen berikut
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 = string.Empty;
private void Set(string value)
{
text = value;
}
private Task SetAsync(string value)
{
text = value;
return Task.CompletedTask;
}
}
Untuk informasi selengkapnya tentang InputText komponen, lihat Blazor input ASP.NET Core.
Untuk contoh lain penggunaan @bind:get dan @bind:set, lihat bagian Ikatan di lebih dari dua komponen nanti di artikel ini.
Razor pengikatan atribut peka huruf besar/kecil:
-
@bind,@bind:event, dan@bind:aftervalid. -
@Bind/@bind:Event/@bind:aftEr(huruf kapital) atau@BIND/@BIND:EVENT/@BIND:AFTER(semua huruf kapital) tidak valid.
Gunakan @bind:get/@bind:set pengubah dan hindari penanganan aktivitas untuk pengikatan data dua arah
Pengikatan data dua arah tidak dimungkinkan untuk diterapkan dengan penanganan aktivitas. Gunakan @bind:get/@bind:set pengubah untuk pengikatan data dua arah.
Pertimbangkan pendekatan disfungsi berikut untuk pengikatan data dua arah menggunakan penanganan aktivitas:
<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;
}
}
Penanganan OnInput aktivitas memperbarui nilai inputValue hingga Long! setelah karakter keempat disediakan. Namun, pengguna dapat terus menambahkan karakter ke nilai elemen di UI. Nilai inputValue tidak terikat kembali ke nilai elemen dengan setiap penekanan tombol. Contoh sebelumnya hanya mampu mengikat data satu arah.
Alasan untuk perilaku ini adalah bahwa Blazor tidak menyadari bahwa kode Anda berniat untuk memodifikasi nilai inputValue dalam penanganan aktivitas.
Blazor tidak mencoba memaksa nilai elemen DOM dan nilai variabel .NET untuk dicocokkan kecuali nilai tersebut terikat dengan @bind sintaks. Dalam versi sebelumnya, Blazorpengikatan data dua arah diimplementasikan dengan mengikat elemen ke properti dan mengontrol nilai properti dengan setter-nya. Dalam ASP.NET Core di .NET 7 atau yang lebih baru, @bind:get/@bind:set sintaks pengubah digunakan untuk menerapkan pengikatan data dua arah, seperti yang ditunjukkan contoh berikutnya.
Pertimbangkan pendekatan yang benar berikut menggunakan @bind:get/@bind:set untuk pengikatan data dua arah:
<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;
}
}
Menggunakan @bind:get/@bind:setpengubah mengontrol nilai yang mendasar melalui inputValue@bind:set dan mengikat nilai inputValue ke nilai elemen melalui @bind:get. Contoh sebelumnya menunjukkan pendekatan yang benar untuk menerapkan pengikatan data dua arah.
Pengikatan ke properti dengan C# get dan set aksesor
C# get dan set aksesor dapat digunakan untuk membuat perilaku format pengikatan kustom, seperti yang ditunjukkan komponen berikut DecimalBinding . Komponen mengikat desimal positif atau negatif dengan hingga tiga tempat desimal ke <input> elemen dengan cara string properti (DecimalValue).
DecimalBinding.razor:
@page "/decimal-binding"
@using System.Globalization
<PageTitle>Decimal Binding</PageTitle>
<h1>Decimal Binding Example</h1>
<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);
}
}
}
}
@page "/decimal-binding"
@using System.Globalization
<PageTitle>Decimal Binding</PageTitle>
<h1>Decimal Binding Example</h1>
<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);
}
}
}
}
@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);
}
}
}
}
@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);
}
}
}
}
@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);
}
}
}
}
@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);
}
}
}
}
Catatan
Di beberapa komponen, pengikatan dua arah ke properti dengan aksesor get/set mengharuskan membuang Task yang dikembalikan oleh EventCallback.InvokeAsync di setter properti. Untuk pengikatan data dua arah, sebaiknya gunakan @bind:get/@bind:set pengubah. Untuk informasi selengkapnya, lihat panduan @bind:get/@bind:set sebelumnya di artikel ini.
Untuk melihat contoh bagaimana Task yang dikembalikan oleh EventCallback.InvokeAsync dibuang di .NET 6 atau yang lebih lama sebelum pengubah @bind:get/@bind:set menjadi fitur kerangka kerja, lihat komponen NestedChild ikatan di lebih dari dua komponen bagian di versi .NET 6 artikel ini.
Catatan
Pengikatan dua arah ke properti dengan get/set aksesor mengharuskan membuang yang Task dikembalikan oleh .EventCallback.InvokeAsync Misalnya, lihat komponen NestedChild ikatan di lebih dari dua komponen bagian. Untuk pengikatan data dua arah di .NET 7 atau yang lebih baru, sebaiknya gunakan @bind:get/@bind:set pengubah, yang dijelaskan dalam .NET 7 atau versi yang lebih baru dari artikel ini.
Pilihan beberapa opsi dengan <select> elemen
Pengikatan mendukung multiple pilihan opsi dengan <select> elemen. Peristiwa ini @onchange menyediakan array elemen yang dipilih melalui argumen peristiwa (ChangeEventArgs). Nilai harus terikat ke jenis array.
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; } = [];
public string[] SelectedCities { get; set; } = [ "bal", "sea" ];
private void SelectedCarsChanged(ChangeEventArgs e)
{
if (e.Value is not null)
{
SelectedCars = (string[])e.Value;
}
}
}
@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;
}
}
}
Untuk informasi tentang bagaimana string dan null nilai kosong ditangani dalam pengikatan data, lihat bagian Opsi elemen pengikatan <select> ke nilai objek null C# .
Opsi elemen pengikatan <select> ke nilai objek null C#
Tidak ada cara yang <select> masuk akal untuk mewakili nilai opsi elemen sebagai nilai objek null C#, karena:
- Atribut HTML tidak dapat memiliki
nullnilai. Yang paling setaranulldengan dalam HTML adalah tidak adanya atribut HTMLvaluedari<option>elemen . - Saat memilih
<option>tanpavalueatribut, browser memperlakukan nilai sebagai konten teks elemen<option>itu.
Kerangka Blazor kerja tidak mencoba menekan perilaku default karena akan melibatkan:
- Membuat rantai solusi kasus khusus dalam kerangka kerja.
- Melanggar perubahan pada perilaku kerangka kerja saat ini.
Yang paling masuk akal null yang setara dalam HTML adalah string.
Blazor Kerangka kerja menangani null konversi string kosong untuk pengikatan dua arah ke <select>nilai .
Nilai yang tidak dapat diurai
Saat pengguna memberikan nilai yang tidak dapat diurai ke elemen terikat data, nilai yang tidak dapat dipisahkan secara otomatis dikembalikan ke nilai sebelumnya saat peristiwa pengikatan dipicu.
Pertimbangkan komponen berikut, di mana <input> elemen terikat ke int jenis dengan nilai 123awal .
UnparsableValues.razor:
@page "/unparsable-values"
<PageTitle>Unparsable Values</PageTitle>
<h1>Unparsable Values Example</h1>
<p>
<label>
inputValue:
<input @bind="inputValue" />
</label>
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
@page "/unparsable-values"
<PageTitle>Unparsable Values</PageTitle>
<h1>Unparsable Values Example</h1>
<p>
<label>
inputValue:
<input @bind="inputValue" />
</label>
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
@page "/unparseable-values"
<p>
<input @bind="inputValue" />
</p>
<p>
<code>inputValue</code>: @inputValue
</p>
@code {
private int inputValue = 123;
}
Pengikatan berlaku untuk peristiwa elemen onchange . Jika pengguna memperbarui nilai entri kotak teks ke 123.45 dan mengubah fokus, nilai elemen dikembalikan ke 123 saat onchange diaktifkan. Ketika nilai 123.45 ditolak demi nilai 123asli , pengguna memahami bahwa nilainya tidak diterima.
oninput Untuk peristiwa (@bind:event="oninput"), pembalikan nilai terjadi setelah penekanan tombol apa pun yang memperkenalkan nilai yang tidak dapat diurai. Saat menargetkan oninput peristiwa dengan intjenis -bound, pengguna dicegah mengetik karakter titik (.). Karakter titik (.) segera dihapus, sehingga pengguna menerima umpan balik langsung bahwa hanya seluruh angka yang diizinkan. Ada skenario di mana mengembalikan nilai pada oninput peristiwa tidak ideal, seperti ketika pengguna harus diizinkan untuk menghapus nilai yang tidak dapat diurai <input> . Alternatifnya meliputi:
- Jangan gunakan peristiwa.
oninputGunakan peristiwa defaultonchange, di mana nilai yang tidak valid tidak dikembalikan hingga elemen kehilangan fokus. - Ikat ke jenis nullable, seperti
int?ataustringdan gunakan@bind:get/@bind:setpengubah (dijelaskan sebelumnya dalam artikel ini) ataugetkustomsetdan aksesor untuk menangani entri yang tidak valid. -
Gunakan komponen input, seperti InputNumber<TValue> atau InputDate<TValue>, dengan validasi formulir. Komponen input bersama dengan komponen validasi formulir menyediakan dukungan bawaan untuk mengelola input yang tidak valid:
- Mengizinkan pengguna untuk memberikan input yang tidak valid dan menerima kesalahan validasi pada EditContext.
- Tampilkan kesalahan validasi di UI tanpa mengganggu pengguna yang memasukkan data formulir web tambahan.
String format
Pengikatan data berfungsi dengan string format tunggal DateTime menggunakan @bind:format="{FORMAT STRING}", di mana {FORMAT STRING} tempat penampung adalah string format. Ekspresi format lainnya, seperti format mata uang atau angka, tidak tersedia saat ini tetapi mungkin ditambahkan dalam rilis mendatang.
DateBinding.razor:
@page "/date-binding"
<PageTitle>Date Binding</PageTitle>
<h1>Date Binding Example</h1>
<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);
}
@page "/date-binding"
<PageTitle>Date Binding</PageTitle>
<h1>Date Binding Example</h1>
<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);
}
@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);
}
@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);
}
@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);
}
@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);
}
Dalam kode sebelumnya, <input> jenis bidang elemen (type atribut) default ke text.
Dapat diubah ke null System.DateTime dan System.DateTimeOffset didukung:
private DateTime? date;
private DateTimeOffset? dateOffset;
Menentukan format untuk date tipe bidang tidak disarankan karena Blazor memiliki dukungan bawaan untuk memformat tanggal. Terlepas dari rekomendasi, hanya gunakan yyyy-MM-dd format tanggal untuk pengikatan agar berfungsi dengan benar jika format disediakan dengan date jenis bidang:
<input type="date" @bind="startDate" @bind:format="yyyy-MM-dd">
Pengikatan dengan parameter komponen
Skenario umum adalah mengikat properti komponen anak ke properti di komponen induknya. Skenario ini disebut ikatan berantai karena beberapa tingkat pengikatan terjadi secara bersamaan.
Anda tidak dapat menerapkan ikatan berantai dengan @bind sintaksis dalam komponen turunan. Penanganan aktivitas dan nilai harus ditentukan secara terpisah untuk mendukung pembaruan properti di induk dari komponen anak. Komponen induk masih memanfaatkan @bind sintaks untuk menyiapkan pengikatan data dengan komponen turunan.
Komponen berikut ChildBind memiliki Year parameter komponen dan EventCallback<TValue>. Menurut konvensi, EventCallback<TValue> untuk parameter harus dinamai sebagai nama parameter komponen dengan akhiran "Changed". Sintaks penamaan adalah {PARAMETER NAME}Changed, di mana {PARAMETER NAME} tempat penampung adalah nama parameter. Dalam contoh berikut, EventCallback<TValue> diberi nama YearChanged.
EventCallback.InvokeAsync memanggil delegasi yang terkait dengan pengikatan dengan argumen yang disediakan dan mengirimkan pemberitahuan peristiwa untuk properti yang diubah.
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 {
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild() =>
await YearChanged.InvokeAsync(Random.Shared.Next(1950, 2021));
}
<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 {
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild() =>
await YearChanged.InvokeAsync(Random.Shared.Next(1950, 2021));
}
<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 {
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild()
{
await YearChanged.InvokeAsync(Random.Shared.Next(1950, 2021));
}
}
<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 {
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
private async Task UpdateYearFromChild()
{
await YearChanged.InvokeAsync(Random.Shared.Next(1950, 2021));
}
}
<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));
}
}
<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));
}
}
Untuk informasi selengkapnya tentang peristiwa dan EventCallback<TValue>, lihat bagian EventCallback dari Blazor peristiwa ASP.NET Core.
Dalam komponen berikut Parent1 , year bidang terikat ke Year parameter komponen anak. Parameter Year dapat diikat karena memiliki peristiwa pendamping YearChanged yang cocok dengan jenis Year parameter.
Parent1.razor:
@page "/parent-1"
<PageTitle>Parent 1</PageTitle>
<h1>Parent Example 1</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private int year = 1979;
private void UpdateYear() => year = Random.Shared.Next(1950, 2021);
}
@page "/parent-1"
<PageTitle>Parent 1</PageTitle>
<h1>Parent Example 1</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private int year = 1979;
private void UpdateYear() => year = Random.Shared.Next(1950, 2021);
}
@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 int year = 1979;
private void UpdateYear()
{
year = Random.Shared.Next(1950, 2021);
}
}
@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 int year = 1979;
private void UpdateYear()
{
year = Random.Shared.Next(1950, 2021);
}
}
@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);
}
}
@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);
}
}
Pengikatan parameter komponen juga dapat memicu @bind:after peristiwa. Dalam contoh berikut, YearUpdated metode dijalankan secara asinkron setelah mengikat Year parameter komponen.
<ChildBind @bind-Year="year" @bind-Year:after="YearUpdated" />
@code {
...
private async Task YearUpdated()
{
... = await ...;
}
}
Menurut konvensi, properti dapat terikat ke penanganan aktivitas yang sesuai dengan menyertakan atribut yang @bind-{PROPERTY}:event ditetapkan ke handler, di mana {PROPERTY} tempat penampung adalah properti .
<ChildBind @bind-Year="year" /> setara dengan penulisan:
<ChildBind @bind-Year="year" @bind-Year:event="YearChanged" />
Dalam contoh yang lebih canggih dan dunia nyata, komponen berikut PasswordEntry :
-
<input>Mengatur nilai elemen kepasswordbidang. - Mengekspos perubahan
Passwordproperti ke komponen induk denganEventCallbackyang meneruskan nilai bidang anakpasswordsaat ini sebagai argumennya. -
onclickMenggunakan peristiwa untuk memicuToggleShowPasswordmetode . Untuk informasi selengkapnya, lihat Blazor peristiwa ASP.NET Core.
Peringatan
Jangan menyimpan rahasia aplikasi, string koneksi, kredensial, kata sandi, nomor identifikasi pribadi (PIN), kode C#/.NET privat, atau kunci/token privat dalam kode sisi klien, yang selalu tidak aman. Di lingkungan pengujian/penahapan dan produksi, kode sisi Blazor server dan API web harus menggunakan alur autentikasi aman yang menghindari mempertahankan kredensial dalam kode proyek atau file konfigurasi. Di luar pengujian pengembangan lokal, sebaiknya hindari penggunaan variabel lingkungan untuk menyimpan data sensitif, karena variabel lingkungan bukanlah pendekatan yang paling aman. Untuk pengujian pengembangan lokal, alat Secret Manager direkomendasikan untuk mengamankan data sensitif. Untuk informasi selengkapnya, lihat Mempertahankan data dan kredensial sensitif dengan aman.
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="password"
value="@password" />
</label>
</p>
</div>
</div>
@code {
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);
}
}
<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="password"
value="@password" />
</label>
</p>
</div>
</div>
@code {
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);
}
}
<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="password"
value="@password" />
</label>
</p>
</div>
</div>
@code {
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);
}
}
<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="password"
value="@password" />
</label>
</p>
</div>
</div>
@code {
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);
}
}
<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="password"
value="@password" />
</label>
</p>
</div>
</div>
@code {
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);
}
}
<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="password"
value="@password" />
</label>
</p>
</div>
</div>
@code {
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);
}
}
Komponen PasswordEntry digunakan dalam komponen lain, seperti contoh komponen berikut PasswordBinding .
PasswordBinding.razor:
@page "/password-binding"
<PageTitle>Password Binding</PageTitle>
<h1>Password Binding Example</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
@page "/password-binding"
<PageTitle>Password Binding</PageTitle>
<h1>Password Binding Example</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<p>
<code>password</code>: @password
</p>
@code {
private string password = "Not set";
}
PasswordBinding Ketika komponen awalnya dirender, password nilai Not set ditampilkan di UI. Setelah penyajian password awal, nilai mencerminkan perubahan yang dilakukan pada Password nilai parameter komponen dalam PasswordEntry komponen.
Catatan
Contoh sebelumnya mengikat kata sandi satu arah dari komponen anak PasswordEntry ke komponen induk PasswordBinding . Pengikatan dua arah bukan persyaratan dalam skenario ini jika tujuannya adalah agar aplikasi memiliki komponen entri kata sandi bersama untuk digunakan kembali di sekitar aplikasi yang hanya meneruskan kata sandi ke induk. Untuk pendekatan yang mengizinkan pengikatan dua arah tanpa menulis langsung ke parameter komponen anak, lihat NestedChild contoh komponen di bagian Ikat di lebih dari dua komponen artikel ini.
Lakukan pemeriksaan atau kesalahan perangkap di handler. Komponen yang direvisi PasswordEntry berikut memberikan umpan balik langsung kepada pengguna jika spasi digunakan dalam nilai kata sandi.
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="password"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
</div>
</div>
@code {
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);
}
}
}
<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="password"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
</div>
</div>
@code {
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);
}
}
}
<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="password"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
</div>
</div>
@code {
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);
}
}
}
Dalam contoh berikut, PasswordUpdated metode dijalankan secara asinkron setelah mengikat Password parameter komponen:
<PasswordEntry @bind-Password="password" @bind-Password:after="PasswordUpdated" />
<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="password"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
</div>
</div>
@code {
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);
}
}
}
<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="password"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
</div>
</div>
@code {
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);
}
}
}
<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="password"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
</div>
</div>
@code {
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);
}
}
}
Mengikat di lebih dari dua komponen
Anda dapat mengikat parameter melalui sejumlah komponen berlapis, tetapi Anda harus menghormati aliran data satu arah:
- Mengubah pemberitahuan mengalir ke hierarki.
- Nilai parameter baru mengalir ke hierarki.
Pendekatan umum dan direkomendasikan adalah hanya menyimpan data yang mendasarinya dalam komponen induk untuk menghindari kebingungan tentang status apa yang harus diperbarui, seperti yang ditunjukkan dalam contoh berikut.
Parent2.razor:
@page "/parent-2"
<PageTitle>Parent 2</PageTitle>
<h1>Parent Example 2</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}";
}
@page "/parent-2"
<PageTitle>Parent 2</PageTitle>
<h1>Parent Example 2</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}";
}
@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}";
}
}
@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}";
}
}
@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}";
}
}
@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}";
}
}
Dalam komponen berikut NestedChild , NestedGrandchild komponen :
- Menetapkan nilai
ChildMessagekeGrandchildMessagedengan@bind:getsintaks. - Pembaruan
GrandchildMessagesaatChildMessageChangeddijalankan dengan@bind:setsintaksis.
Sebelum rilis .NET 7, pengikatan dua arah antar komponen menggunakan aksesor get/set dengan properti ketiga yang membuang Task yang dikembalikan oleh EventCallback.InvokeAsync dalam setternya. Untuk melihat contoh pendekatan ini untuk .NET 6 atau yang lebih lama sebelum pengubah @bind:get/@bind:set menjadi fitur kerangka kerja, lihat komponen NestedChild bagian ini di versi .NET 6 artikel ini.
Menghindari perubahan nilai parameter komponen secara langsung diperlukan karena dapat secara efektif mengubah keadaan (state) induk dari komponen anak. Ini dapat mengganggu Blazorproses deteksi perubahan dan memicu siklus render ekstra karena parameter dimaksudkan untuk menjadi input, mereka tidak dimaksudkan untuk menjadi status yang dapat diubah. Dalam skenario berantai di mana data diteruskan di antara komponen, dengan langsung menulis ke parameter komponen dapat menyebabkan efek yang tidak diinginkan, seperti rerendering tak terbatas yang membuat aplikasi berhenti merespons.
@bind:get
/
@bind:set sintaks memungkinkan Anda untuk:
- Hindari membuat suatu properti tambahan yang hanya ada untuk meneruskan nilai dan panggilan balik dalam rantai komponen, yang diperlukan sebelum rilis .NET 7.
- Mencegat dan mengubah nilai sebelum diterapkan.
- Jaga parameter tetap tidak dapat diubah pada anak, sambil tetap mendukung pengikatan dua arah.
Analogi yang berguna adalah elemen HTML <input> yang melacak status nilai berikut:
-
defaultValue: Seperti parameter komponen yang diterima dari induk. -
value: Seperti keadaan saat ini di dalam komponen.
Jika Anda mengubah defaultValue secara langsung, Anda melanggar kontrak. Sebaliknya, status ini dipisahkan, dan hanya yang value diperbarui melalui cara terkontrol setelah render awal elemen. Penalaran yang sama berlaku untuk parameter komponen, dan menggunakan @bind:get/@bind:set sintaksis menghindari potensi efek penyajian yang tidak diinginkan yang terkait dengan penulisan langsung ke parameter komponen.
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}");
}
<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}");
}
<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}");
}
}
<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}");
}
}
<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}");
}
}
<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}");
}
}
Peringatan
Umumnya, hindari membuat komponen yang menulis langsung ke parameter komponen mereka sendiri. Komponen sebelumnya NestedChild menggunakan BoundValue properti alih-alih menulis langsung ke parameternya ChildMessage . Untuk informasi selengkapnya, lihat Menghindari penimpaan parameter di ASP.NET Core Blazor.
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}");
}
<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}");
}
<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}");
}
}
<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}");
}
}
<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}");
}
}
<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}");
}
}
Untuk pendekatan alternatif yang cocok untuk berbagi data dalam memori dan di seluruh komponen yang belum tentu ditumpuk, lihat ringkasan manajemen status ASP.NET CoreBlazor.
Bidang terikat atau pohon ekspresi properti
Untuk memfasilitasi interaksi yang lebih dalam dengan pengikatan, Blazor memungkinkan Anda mengambil pohon ekspresi bidang atau properti terikat. Ini dicapai dengan menentukan properti dengan bidang atau nama properti yang diabaikan dengan Expression. Untuk bidang atau properti tertentu bernama {FIELD OR PROPERTY NAME}, properti pohon ekspresi yang sesuai diberi nama {FIELD OR PROPERTY NAME}Expression.
Komponen berikut ChildParameterExpression mengidentifikasi Year model ekspresi dan nama bidang.
FieldIdentifier, yang digunakan untuk mendapatkan model dan nama bidang, secara unik mengidentifikasi satu bidang yang dapat diedit. Ini mungkin sesuai dengan properti pada objek model atau dapat berupa nilai bernama lainnya. Penggunaan ekspresi parameter berguna saat membuat komponen validasi kustom, yang tidak dicakup oleh dokumentasi Microsoft Blazor tetapi ditangani oleh banyak sumber daya pihak ketiga.
ChildParameterExpression.razor:
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized() =>
yearField = FieldIdentifier.Create(YearExpression);
}
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized() =>
yearField = FieldIdentifier.Create(YearExpression);
}
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized()
{
yearField = FieldIdentifier.Create(YearExpression);
}
}
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized()
{
yearField = FieldIdentifier.Create(YearExpression);
}
}
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized()
{
yearField = FieldIdentifier.Create(YearExpression);
}
}
@using System.Linq.Expressions
<ul>
<li>Year model: @yearField.Model</li>
<li>Year field name: @yearField.FieldName</li>
</ul>
@code {
private FieldIdentifier yearField;
[Parameter]
public int Year { get; set; }
[Parameter]
public EventCallback<int> YearChanged { get; set; }
[Parameter]
public Expression<Func<int>> YearExpression { get; set; } = default!;
protected override void OnInitialized()
{
yearField = FieldIdentifier.Create(YearExpression);
}
}
Parent3.razor:
@page "/parent-3"
<PageTitle>Parent 3</PageTitle>
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
@page "/parent-3"
<PageTitle>Parent 3</PageTitle>
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
@page "/parent-3"
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
@page "/parent-3"
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
@page "/parent-3"
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
@page "/parent-3"
<h1>Parent Example 3</h1>
<p>Parent <code>year</code>: @year</p>
<ChildParameterExpression @bind-Year="year" />
@code {
private int year = 1979;
}
Sumber Daya Tambahan:
- Deteksi perubahan parameter dan panduan tambahan tentang Razor penyajian komponen
- ringkasan formulir ASP.NET Core Blazor
- Pengikatan ke tombol radio dalam formulir
-
Opsi pengikatan
InputSelectke nilai objeknullC# -
penanganan peristiwa ASP.NET Core Blazor :
EventCallbackbagian -
Blazor (
dotnet/blazor-samplescara mengunduh)
ASP.NET Core