Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Jegyzet
Ez nem a cikk legújabb verziója. Az aktuális kiadásról a cikk .NET 10-es verziójában olvashat.
Figyelmeztetés
A ASP.NET Core ezen verziója már nem támogatott. További információ: .NET és .NET Core támogatási szabályzat. Tekintse meg az aktuális kiadást a jelen cikk .NET 9-es verziójában.
Ez a cikk az Razor-alkalmazások Blazor összetevőinek és DOM-elemeinek adatkötési funkcióit ismerteti.
Kötési funkciók
Razor összetevők adatkötési funkciókat biztosítanak a @bindRazor irányelv attribútummal egy mező, egy tulajdonság vagy egy Razor kifejezés értékével.
"A következő példa kötést mutat be:"
- Egy
<input>elemértéket a C#inputValuemezőhöz. - Egy második
<input>elemértéket a C#InputValuetulajdonsághoz.
Ha egy <input> elem elveszíti a fókuszt, a kötött mező vagy tulajdonság frissül.
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; }
}
A szövegdoboz csak az összetevő renderelésekor frissül a felhasználói felületen, nem pedig a mező vagy a tulajdonság értékének módosítására adott válaszként. Mivel az összetevők az eseménykezelő kód végrehajtása után jelennek meg, a mező- és tulajdonságfrissítések általában közvetlenül az eseménykezelő aktiválása után jelennek meg a felhasználói felületen.
Az adatkötés HTML-ben való összeállításának szemléltetésére az alábbi példa a InputValue tulajdonságot a második <input> elem value és onchange attribútumaihoz (change) köti.
A következő példában szereplő második <input> elem egy koncepcióbemutató, és nem azt hivatott javasolni, hogyan kell adatokat kötni Razor összetevőkben.
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; }
}
A BindTheory összetevő megjelenítésekor a HTML bemutató value elemének <input> a InputValue tulajdonságból származik. Amikor a felhasználó beír egy értéket a szövegmezőbe, és módosítja az elem fókuszát, a rendszer aktiválja a onchange eseményt, és a InputValue tulajdonság a módosított értékre van állítva. A valóságban a kódvégrehajtás összetettebb, mert @bind kezeli azokat az eseteket, amikor típuskonverziókat hajtanak végre. Általánosságban @bind társítja egy kifejezés aktuális értékét a value<input> attribútumával, és a regisztrált kezelővel kezeli a módosításokat.
Tulajdonság vagy mező kötése különböző DOM-eseményekhez a @bind:event="{EVENT}" attribútum használatával a {EVENT} helyőrzőhöz tartozó DOM-eseménnyel. Az alábbi példa az InputValue tulajdonságot az <input> elem értékéhez köti, amikor az elem oninput eseménye (input) aktiválódik. A onchange eseménytől (change) ellentétben, amely akkor aktiválódik, amikor az elem elveszíti a fókuszt, oninput (input) aktiválódik, amikor a szövegdoboz értéke megváltozik.
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 attribútumkötés kis- és nagybetű érzékeny:
-
@bindés@bind:eventérvényesek. -
@Bind/@Bind:Event(nagybetűkBésE) vagy@BIND/@BIND:EVENT(az összes nagybetű) érvénytelenek.
Az aszinkron logika kötés utáni végrehajtásához használja a @bind:after="{DELEGATE}", ahol a {DELEGATE} helyőrző egy C# delegált (metódus). A hozzárendelt C#-delegált csak akkor hajtódik végre, ha a kötött értéket szinkron módon rendelik hozzá.
Az eseményvisszahívási paraméter (EventCallback/EventCallback<T>) használata @bind:after nem támogatott. Ehelyett adjon át egy metódust, amely egy Action vagy Task ad vissza @bind:after.
Az alábbi példában:
- Minden
<input>elemvalueszinkron módon van kötve asearchTextmezőhöz. - A
PerformSearchmetódus aszinkron módon fut:- Amikor az első mező elveszíti a fókuszt (
onchangeeseményt) az érték módosítása után. - Minden billentyűleütés (
oninputesemény) után a második mezőben.
- Amikor az első mező elveszíti a fókuszt (
-
PerformSearchaszinkron metódussal (FetchAsync) meghív egy szolgáltatást a keresési eredmények visszaadásához.
@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);
}
További példák
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; }
}
A InputText összetevőről további információt ASP.NET Core Blazor bemeneti összetevőkcímű cikkben talál.
Az összetevők támogatják a kétirányú adatkötést azáltal, hogy @bind attribútumpárokat határoznak meg egy :get vagy :set módosítóval. Az alábbi példákban szereplő {PARAMETER} helyőrző egy összetevőparaméter kötésére szolgál:
-
@bind:get/@bind-{PARAMETER}:get: A kötés értékét adja meg. -
@bind:set/@bind-{PARAMETER}:set: Az érték módosításának visszahívását adja meg.
A :get és :set módosítókat mindig együtt használják.
A :get/:set kötéssel reagálhat egy értékváltozásra, mielőtt alkalmazva lenne a DOM-ra, és szükség esetén módosíthatja az alkalmazott értéket. Míg @bind:event="{EVENT}" attribútumkötés esetén, ahol a {EVENT} helyőrző EGY DOM-esemény, a DOM frissítése után megkapja az értesítést, és nincs kapacitás az alkalmazott érték módosítására kötés közben.
Az alábbi
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;
}
}
A InputText összetevőről további információt ASP.NET Core Blazor bemeneti összetevőkcímű cikkben talál.
A
Razor attribútumkötés kis- és nagybetű érzékeny:
-
@bind,@bind:eventés@bind:afterérvényesek. -
@Bind/@bind:Event/@bind:aftEr(nagybetűk) vagy@BIND/@BIND:EVENT/@BIND:AFTER(az összes nagybetű) érvénytelenek.
Használjon @bind:get/@bind:set módosítókat, és kerülje az eseménykezelőket a kétirányú adatkötéshez
A kétirányú adatkötés nem valósítható meg eseménykezelővel. Kétirányú adatkötéshez használjon @bind:get/@bind:set módosítókat.
Vegye figyelembe az alábbi diszfunkcionális megközelítést, kétirányú adatkötéshez egy eseménykezelő használatával:
<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;
}
}
Az OnInput eseménykezelő egy negyedik karakter megadása után a inputValue értékét Long!-re frissíti. A felhasználó azonban továbbra is hozzáadhat karaktereket az elemértékhez a felhasználói felületen. A inputValue értéke nem lesz visszakötve az elem értékéhez az egyes billentyűleütésekkel. Az előző példa csak egyirányú adatkötésre képes.
Ennek a viselkedésnek az az oka, hogy Blazor nem tudja, hogy a kód módosítani kívánja a inputValue értékét az eseménykezelőben.
Blazor nem próbálja kényszeríteni a DOM elemértékeket és a .NET változóértékeket az egyezésre, kivéve, ha @bind szintaxishoz vannak kötve. A Blazorkorábbi verzióiban a kétirányú adatkötést úgy valósítja meg, hogy az elemet egy tulajdonsághoz köti, és a tulajdonság értékét a beállításiszabályozza. A .NET 7-es vagy újabb verziójában ASP.NET Core-ban @bind:get/@bind:set módosító szintaxist használunk a kétirányú adatkötés implementálásához, amint azt a következő példa is mutatja.
✔️ Fontolja meg az alábbi helyes megközelítést a @bind:get/@bind:set használatával kétirányú adatkötéshez:
<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;
}
}
A @bind:get/@bind:set módosítók egyszerre vezérlik a inputValue mögöttes értékét @bind:set segítségével, és kötik az inputValue értékét az elem értékéhez @bind:getáltal. Az előző példa a kétirányú adatkötés implementálásának helyes megközelítését mutatja be.
Kötés egy tulajdonsághoz C# get és set tartozékokkal
C# get és set tartozékokkal egyéni kötésformázási viselkedést hozhat létre, amint azt az alábbi DecimalBinding összetevő szemlélteti. Az összetevő egy pozitív vagy negatív tizedesjegyet legfeljebb három tizedesjegygel köt össze egy <input> elemhez egy string tulajdonság (DecimalValue) segítségével.
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);
}
}
}
}
Jegyzet
Több összetevő esetében a tulajdonsághoz való kétirányú kötéshez get/set elérőkkel el kell vetnie a tulajdonság beállítójában a Task által visszaadott EventCallback.InvokeAsync-at. Kétirányú adatkötéshez @bind:get/@bind:set módosítókat javasoljuk. További információért lásd a @bind:get/@bind:set útmutatót a cikk korábbi részében.
Ha szeretne egy példát látni arra, hogy a Task által visszaadott EventCallback.InvokeAsync hogyan lesz elvetve a .NET 6-ban vagy korábban, mielőtt @bind:get/@bind:set módosítók keretrendszerfunkcióvá váltak, tekintse meg a Kötés NestedChild összetevőjét a jelen cikk .NET 6-verziójának több mint két összetevőre szakaszában.
Jegyzet
Az get/set tartozékokkal rendelkező tulajdonsághoz való kétirányú kötéshez el kell vetni a Taskáltal visszaadott EventCallback.InvokeAsync. Példáért lásd a @bind:get/@bind:set .
Több lehetőség kijelölése <select> elemekkel
A kötés támogatja multiple beállítás kiválasztását <select> elemekkel. A @onchange esemény a kiválasztott elemek tömböt biztosít eseményargumentumok (ChangeEventArgs)segítségével. Az értéknek tömbtípushoz kell kötődnie.
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;
}
}
}
Az üres sztringek és
<select> elemopciók kötése C# objektum null értékeihez
Az <select> elem opció értékét nem lehet C# objektum null értékként megjeleníteni, mert:
- A HTML-attribútumok nem tartalmazhatnak
nullértékeket. A HTML-ben anulllegközelebbi megfelelője a HTMLvalueattribútum hiánya a<option>elemnél. - Ha egy
<option>attribútum nélkülivalue-et választ ki, a böngésző az értéket az adott elem<option>kezeli.
A Blazor-keretrendszer nem próbálja meg letiltani az alapértelmezett viselkedést, mert az a következőket foglalja magában:
- Speciális eseti áthidaló megoldások láncának létrehozása a keretrendszerben.
- A jelenlegi keretrendszer viselkedésének változásai, amelyek megszakítják a kompatibilitást.
A HTML-ben a legvalószínűbb null egy üres karakterláncvalue. A Blazor keretrendszer kezeli a null üres karakterláncra történő konvertálását a <select>értékéhez való kétirányú kötés során.
Nem elemezhető értékek
Ha egy felhasználó elválaszthatatlan értéket biztosít egy adathoz kötött elemnek, a rendszer automatikusan visszaállítja a nem elemezhető értéket a korábbi értékre a kötési esemény aktiválásakor.
Vegye figyelembe a következő összetevőt, amelyben egy <input> elem egy int típusú, 123kezdeti értékkel rendelkező típushoz van kötve.
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;
}
A kötés az elem onchange eseményére vonatkozik. Ha a felhasználó frissíti a szövegdobozban lévő értéket 123.45-ra, és módosítja a fókuszt, az elem értéke visszaáll 123-re, amikor a onchange esemény bekövetkezik. Ha a 123.45 értéke a 123eredeti értéke mellett kerül elutasításra, a felhasználó tudomásul veszi, hogy az érték nem lett elfogadva.
Az oninput esemény (@bind:event="oninput") esetében érték-visszafordulás következik be minden olyan billentyűleütés után, amely értelmezhetetlen értéket vezet be. Ha a oninput eseményt intkötött típussal célozza meg, a felhasználó nem tud begépelni egy pont (.) karaktert. A pont (.) karakter azonnal el lesz távolítva, így a felhasználó azonnali visszajelzést kap arról, hogy csak egész számok engedélyezettek. Vannak olyan helyzetek, amikor az oninput esemény értékének visszaállítása nem ideális, például amikor a felhasználónak lehetősége legyen egy értelmezhetetlen <input> érték törlésére. Alternatív lehetőségek:
- Ne használja a
oninputeseményt. Használja az alapértelmezettonchangeeseményt, ahol a rendszer nem ad vissza érvénytelen értéket, amíg az elem nem veszíti el a fókuszt. - Kötődés null értékű típushoz, például
int?vagystring, és használja a@bind:get/@bind:setmódosítókat (ahogyan a cikk korábbi részében leírtuk), vagy használjon kötődést egy olyan tulajdonsághoz, amely egyénigetéssethozzáférési logikával rendelkezik az érvénytelen bejegyzések kezelésére. - A bemeneti összetevőt, például InputNumber<TValue> vagy InputDate<TValue>, használd űrlapérvényesítési. A bemeneti összetevők és az űrlap-érvényesítési összetevők beépített támogatást nyújtanak az érvénytelen bemenetek kezeléséhez:
- Engedélyezze a felhasználónak, hogy érvénytelen bemenetet adjon meg, és érvényesítési hibákat kapjon a társított EditContext.
- Érvényesítési hibák megjelenítése a felhasználói felületen anélkül, hogy zavarja a felhasználót a további webűrlap-adatok megadásában.
Sztringek formázása
Az adatkapcsolat egyetlen DateTime formátumú karakterlánccal működik @bind:format="{FORMAT STRING}"használatával, ahol a {FORMAT STRING} egy formátum karakterlánc helyett álló érték. Más formátumkifejezések, például pénznem- vagy számformátumok jelenleg nem érhetők el, de egy későbbi kiadásban is hozzáadhatók.
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);
}
Az előző kódban az <input> elem mezőtípusa (type attribútum) alapértelmezés szerint text.
A null értékű System.DateTime és System.DateTimeOffset támogatottak:
private DateTime? date;
private DateTimeOffset? dateOffset;
A date mezőtípus formátumának megadása nem ajánlott, mert Blazor beépített támogatással rendelkezik a dátumok formázásához. A javaslat ellenére csak akkor használja a yyyy-MM-dd dátumformátumot a kötés helyes működéséhez, ha a date mezőtípushoz megadott formátumot adja meg:
<input type="date" @bind="startDate" @bind:format="yyyy-MM-dd">
Kötés összetevőparaméterekkel
Gyakori forgatókönyv egy gyermekösszetevő tulajdonságának kötése a szülőösszetevő egyik tulajdonságához. Ezt a forgatókönyvet láncolt kötésnek nevezzük, mert egyszerre több kötési szint is előfordul.
Nem implementálhat láncolt kötéseket @bind szintaxissal egy alösszetevőben. Az eseménykezelőt és az értéket külön kell megadni a szülő tulajdonságának a gyermekösszetevőből való frissítéséhez. A szülőösszetevő továbbra is @bind szintaxissal állítja be az adatkötést a gyermekösszetevővel.
Az alábbi ChildBind összetevő egy Year összetevőparaméterrel és egy EventCallback<TValue>rendelkezik. Konvenció szerint a paraméter EventCallback<TValue> egy "Changed" utótaggal rendelkező összetevőparaméter-névként kell elneveznünk. Az elnevezési szintaxis {PARAMETER NAME}Changed, ahol a {PARAMETER NAME} helyőrző a paraméter neve. Az alábbi példában a EventCallback<TValue> neve YearChanged.
EventCallback.InvokeAsync meghívja a kötéshez társított meghatalmazottat a megadott argumentummal, és eseményértesítést küld a módosított tulajdonságról.
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));
}
}
Az eseményekkel és EventCallback<TValue>kapcsolatos további információkért tekintse meg a cikk Blazor szakaszát.
Az alábbi Parent1 összetevőben a year mező a gyermekösszetevő Year paraméteréhez van kötve. A Year paraméter azért köthető össze, mert a YearChanged paraméter típusának megfelelő társ Year eseményrel rendelkezik.
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);
}
}
Az összetevőparaméter kötése @bind:after eseményeket is aktiválhat. Az alábbi példában a YearUpdated metódus aszinkron módon hajtja végre a Year összetevőparaméter kötése után.
<ChildBind @bind-Year="year" @bind-Year:after="YearUpdated" />
@code {
...
private async Task YearUpdated()
{
... = await ...;
}
}
Konvenció szerint egy tulajdonság egy megfelelő eseménykezelőhöz köthető, ha egy @bind-{PROPERTY}:event attribútumot rendel hozzá a kezelőhöz, ahol a {PROPERTY} helyőrző a tulajdonság.
<ChildBind @bind-Year="year" /> az írásnak felel meg:
<ChildBind @bind-Year="year" @bind-Year:event="YearChanged" />
Egy kifinomultabb és valóságközelibb példa esetében a következő PasswordEntry összetevő:
- Egy
<input>elem értékét egypasswordmezőre állítja. - A
Passwordtulajdonság változásait egy szülőkomponens számára úgy teszi elérhetővé, hogy a gyermekEventCallbackmezőjének aktuális értékét argumentumként adja át egypassword-en keresztül. - A
onclickmetódus aktiválásához használja azToggleShowPasswordeseményt. További információkért lásd: ASP.NET Core Blazor eseménykezelés.
Figyelmeztetés
Ne tárolja az alkalmazás titkos kulcsait, kapcsolati sztringeket, hitelesítő adatokat, jelszavakat, személyes azonosítószámokat (PIN-eket), titkos C#/.NET-kódot vagy titkos kulcsokat/jogkivonatokat az ügyféloldali kódban, amely mindig nem biztonságos. Tesztelési/előkészítési és éles környezetekben a kiszolgálóoldali Blazor kód- és webes API-knak biztonságos hitelesítési folyamatokat kell használniuk, amelyek nem őrzik meg a hitelesítő adatokat a projektkódban vagy a konfigurációs fájlokban. A helyi fejlesztési tesztelésen kívül javasoljuk, hogy kerülje a környezeti változók bizalmas adatok tárolására való használatát, mivel a környezeti változók nem a legbiztonságosabb megközelítések. A helyi fejlesztési teszteléshez a Secret Manager eszköz ajánlott bizalmas adatok védelméhez. További információ: Bizalmas adatok és hitelesítő adatok biztonságos karbantartása.
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);
}
}
A PasswordEntry összetevőt egy másik összetevő használja, például az alábbi PasswordBinding összetevő-példában.
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";
}
A PasswordBinding összetevő kezdeti megjelenítésekor a felhasználói felületen megjelenik a passwordNot set értéke. A kezdeti renderelés után a password értéke a Password összetevő PasswordEntry összetevő paraméterértékének változásait tükrözi.
Jegyzet
Az előző példa egyirányúan köti a jelszót a gyermek PasswordEntry összetevőtől a szülő PasswordBinding összetevőhöz. Ebben a forgatókönyvben nem követelmény a kétirányú kötés, ha a cél az, hogy az alkalmazás egy megosztott jelszóbejegyzési összetevővel rendelkezzen az alkalmazás körül, amely csupán átadja a jelszót a szülőnek. Ha olyan megközelítést szeretne használni, amely lehetővé teszi a kétirányú kötést anélkül, hogy
Ellenőrzéseket vagy hibakezelést végez a kezelőben. Az alábbi módosított PasswordEntry összetevő azonnali visszajelzést nyújt a felhasználónak, ha a jelszó értékében szóközt használnak.
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);
}
}
}
Az alábbi példában a PasswordUpdated metódus aszinkron módon hajtja végre a Password összetevőparaméter kötése után:
<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);
}
}
}
Kötés több mint két összetevő között
A paramétereket tetszőleges számú beágyazott összetevőn keresztül kötheti, de tiszteletben kell tartania az egyirányú adatáramlást:
- Az értesítések felfelé áramlanak a hierarchiában.
- Az új paraméterértékek leáramlanak a hierarchiában.
Gyakori és ajánlott módszer, hogy a mögöttes adatokat csak a szülőösszetevőben tárolja, hogy elkerülje a frissítési állapotokkal kapcsolatos félreértéseket, ahogy az alábbi példában is látható.
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}";
}
}
Az alábbi NestedChild összetevőben a NestedGrandchild összetevő:
- A
ChildMessageértékétGrandchildMessage-hez rendeli hozzá@bind:getszintaxissal. - Frissíti
GrandchildMessage-t, amikorChildMessageChanged@bind:setszintaxissal végrehajt.
A .NET 7 kiadása előtt az összetevők közötti kétirányú kötés a get,/, ésset tartozékokat használja, amelyek egy harmadik tulajdonságot alkalmaznak, amely a beállítóban elveti a Task által visszaadott EventCallback.InvokeAsync adatokat. .NET 6 vagy korábbi verziók esetén ennek a megközelítésnek egy példáját, mielőtt a @bind:get/@bind:set módosítók a keretrendszer részévé váltak, találja meg a cikk .NET 6-os verziójának ebben a részében található NestedChild komponensnél.
Annak oka, hogy el kell kerülni az összetevőparaméter értékének közvetlen módosítását, az, hogy ez hatékonyan a szülő állapotának a gyermekösszetevő általi módosítását eredményezheti. Ez zavarhatja Blazora változásészlelési folyamatot, és további renderelési ciklusokat indíthat el, mivel a paraméterek bemenetek, és nem mutable állapotúak. Olyan láncolt forgatókönyvekben, amelyekben az adatok az összetevők között kerülnek továbbításra, az összetevőparaméterre való közvetlen írás nem kívánt effektusokhoz vezethet, például az alkalmazást lefagyó végtelen rerenderekhez.
@bind:get
/
@bind:set szintaxissal a következőt végezheti el:
- Ne hozzon létre olyan extra tulajdonságot, amely csak az értékek és visszahívások láncolt összetevők közötti továbbításához létezik, amelyre a .NET 7 kiadása előtt volt szükség.
- Az értékek elfogása és átalakítása mielőtt alkalmaznák őket.
- A paraméter nem módosítható a gyermekben, miközben továbbra is támogatja a kétirányú kötést.
Hasznos analógia a HTML-elem<input>, amely a következő értékállapotokat követi nyomon:
-
defaultValue: A szülőtől kapott összetevőparaméterhez hasonlóan. -
value: Az összetevő aktuális állapotához hasonlóan.
Ha közvetlenül mutál defaultValue , megszegi a szerződést. Ehelyett ezek az állapotok külön maradnak, és csak a value szabályozott eszközökkel frissülnek az elem kezdeti renderelése után. Ugyanez az érvelés vonatkozik az összetevőparaméterekre is, és a szintaxis használata @bind:get/@bind:set elkerüli a közvetlenül az összetevőparaméterekre való írással járó nem szándékos renderelési hatásokat.
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}");
}
}
Figyelmeztetés
Általában ne hozzon létre olyan összetevőket, amelyek közvetlenül a saját összetevőparamétereikre írnak. Az előző NestedChild összetevő egy BoundValue tulajdonságot használ ahelyett, hogy közvetlenül a ChildMessage paraméterbe ír. További információ: A paraméterek felülírásának elkerülése 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}");
}
}
Az adatok memóriabeli és nem feltétlenül beágyazott összetevők közötti megosztására alkalmas alternatív megközelítésért tekintse meg a ASP.NET Core Blazor állapotkezelésének áttekintését.
Kötött mező vagy tulajdonságkifejezés fa
A kötésekkel való mélyebb interakciók megkönnyítése érdekében Blazor lehetővé teszi egy kötött mező vagy tulajdonság kifejezésfájának rögzítését. Ez úgy érhető el, hogy egy olyan tulajdonságot határoz meg, amelynek a mezője vagy a tulajdonság neve Expressionutótaggal van elnevítve. Az adott {FIELD OR PROPERTY NAME}nevű mező vagy tulajdonság esetében a megfelelő kifejezésfa tulajdonság neve {FIELD OR PROPERTY NAME}Expression.
Az alábbi ChildParameterExpression összetevő azonosítja a Year kifejezés modelljét és mezőnevét. A modell és a mezőnév lekérésére használt FieldIdentifieregyedileg azonosítja a szerkeszthető egyetlen mezőt. Ez megfelelhet egy modellobjektum tulajdonságának, vagy bármely más elnevezett értéknek. A paraméterek kifejezésének használata akkor hasznos, ha egyéni érvényesítési összetevőket hoz létre, amelyeket nem fed le a Microsoft Blazor dokumentációja, de számos külső erőforrás foglalkozik vele.
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;
}
További erőforrások
- Paraméterváltozás-észlelés és további útmutatás Razor komponens megjelenítéséhez
- ASP.NET Core Blazor űrlapok áttekintése
- Választógombokhoz kötés egy űrlapon
-
Kötési lehetőségek
InputSelecta C#-objektumnullértékeihez -
ASP.NET Core Blazor eseménykezelés:
EventCallbackszakasz -
Blazor GitHub adattár mintái (
dotnet/blazor-samples) (hogyan lehet letölteni)