ASP.NET Core Blazor データ バインディング
注意
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
警告
このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
重要
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。
現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
この記事では、Blazor アプリの Razor コンポーネントとドキュメント オブジェクト モデル (DOM) 要素のデータ バインディング機能について説明します。
バインディング機能
Razor コンポーネントには、フィールド、プロパティ、または Razor 式の値が含まれる @bind
Razor ディレクティブ属性を使用したデータ バインディング機能が用意されています。
以下の例で行われているバインドは次のとおりです。
<input>
要素の値を C#inputValue
フィールドに。- 2 番目の
<input>
要素の値を C#InputValue
プロパティに。
<input>
要素がフォーカスを失うと、そのバインドされたフィールドまたはプロパティが更新されます。
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; }
}
テキスト ボックスは、フィールドまたはプロパティの値の変更に対する応答としてではなく、コンポーネントがレンダリングされたときにのみ UI が更新されます。 コンポーネントはイベント ハンドラーのコードが実行された後に自身をレンダリングするため、通常は、イベント ハンドラーがトリガーされた直後に、フィールドとプロパティの更新が UI に反映されます。
次の例では、データ バインディングが HTML でどのように構成されるかを示すために、InputValue
プロパティを 2 番目の <input>
要素の value
および onchange
属性 (change
) にバインドします。 次の例の 2 番目の <input>
要素は概念実証であって、Razor コンポーネントでデータをバインドする方法を提案するものではありません。
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
コンポーネントがレンダリングされると、HTML 実証の <input>
要素の value
は InputValue
プロパティから取得されます。 ユーザーがテキスト ボックスに値を入力し、要素のフォーカスを変更すると、onchange
イベントが発生し、InputValue
プロパティは変更された値に設定されます。 実際には、@bind
で扱われるのは、型変換が行われるケースであるため、コード生成はより複雑になります。 一般に、@bind
は式の現在の値と <input>
の value
属性を関連付け、登録されたハンドラーを使って変更を処理します。
{EVENT}
プレースホルダーに DOM イベントを使用した @bind:event="{EVENT}"
属性を含めることにより、他の DOM イベントのプロパティまたはフィールドをバインドします。 次の例では、<input>
要素の oninput
イベント (input
) がトリガーされたときに、その要素の値に InputValue
プロパティをバインドします。 要素がフォーカスを失ったときに発生する onchange
イベント (change
) とは異なり、テキスト ボックスの値が変更されたときに oninput
(input
) が発生します。
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 属性バインディングでは大文字と小文字が区別されます。
@bind
および@bind:event
が有効です。@Bind
/@Bind:Event
(大文字のB
とE
) または@BIND
/@BIND:EVENT
(すべて大文字) は無効です。
バインド後に非同期ロジックを実行するには、 @bind:after="{DELEGATE}"
を使用します。ここで、 {DELEGATE}
プレースホルダーは C# デリゲート (メソッド) です。 割り当てられた C# デリゲートは、バインドされた値が同期的に割り当てられるまで実行されません。
イベント コールバック パラメーター (EventCallback
/EventCallback<T>
) を @bind:after
で使用することはサポートされていません。 代わりに、Action または Task を返すメソッドを @bind:after
に渡します。
次に例を示します。
- 各
<input>
要素のvalue
は、searchText
フィールドに同期的にバインドされます。 - メソッドは
PerformSearch
非同期的に実行されます。- 値が変更された後、最初のボックスがフォーカス (
onchange
イベント) を失った場合。 - 2 番目のボックスの各キーストローク (
oninput
イベント) の後。
- 値が変更された後、最初のボックスがフォーカス (
PerformSearch
では、非同期メソッド (FetchAsync
) を使用してサービスを呼び出し、検索結果を返します。
@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);
}
追加の例
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; }
}
InputText
コンポーネントについて詳しくは、「ASP.NET Core Blazor の入力コンポーネント」を参照してください。
コンポーネントでは、パラメーターのペアを定義することで、双方向のデータ バインディングがサポートされます。
@bind:get
: バインドする値を指定します。@bind:set
: 値が変更されたときのコールバックを指定します。
@bind:get
修飾子と @bind:set
修飾子は常に一緒に使用されます。
例
BindGetSet.razor
:
@page "/bind-get-set"
@using Microsoft.AspNetCore.Components.Forms
<h1>Bind Get Set Examples</h1>
<h2>Elements</h2>
<input type="text" @bind:get="text" @bind:set="(value) => { text = value; }" />
<input type="text" @bind:get="text" @bind:set="Set" />
<input type="text" @bind:get="text" @bind:set="SetAsync" />
<h2>Components</h2>
<InputText @bind-Value:get="text" @bind-Value:set="(value) => { text = value; }" />
<InputText @bind-Value:get="text" @bind-Value:set="Set" />
<InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />
@code {
private string text = "";
private void Set(string value)
{
text = value;
}
private Task SetAsync(string value)
{
text = value;
return Task.CompletedTask;
}
}
InputText
コンポーネントについて詳しくは、「ASP.NET Core Blazor の入力コンポーネント」を参照してください。
@bind:get
と @bind:set
の別の使用例については、この記事の後半の「3 つ以上のコンポーネント間でバインドする」セクションを参照してください。
Razor 属性バインディングでは大文字と小文字が区別されます。
@bind
、@bind:event
、@bind:after
が有効です。@Bind
/@bind:Event
/@bind:aftEr
(大文字) または@BIND
/@BIND:EVENT
/@BIND:AFTER
(すべてが大文字) は使用できません。
@bind:get
/@bind:set
修飾子を使用し、双方向データ バインディングのイベント ハンドラーを回避する
双方向データ バインディングをイベント ハンドラーで実装することはできません。 双方向データ バインディングには @bind:get
/@bind:set
修飾子を使用します。
イベント ハンドラーを使用する双方向データ バインディングでは、次の "機能不全のアプローチ" を検討してください。
<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;
}
}
OnInput
イベント ハンドラーは、4 番目の文字が指定された後に inputValue
の値を Long!
に更新します。 ただし、ユーザーは UI の要素値に文字を追加し続けることができます。 inputValue
の値は、キーストロークごとに要素の値にバインドされません。 上記の例では、一方向のデータ バインディングのみが可能です。
この動作の理由は、コードがイベント ハンドラーの inputValue
の値を変更しようとしていることを Blazor が認識していないためです。 DOM 要素の値と .NET 変数の値が @bind
構文でバインドされていない限り、Blazor はそれらを強制的に一致させようとはしません。 以前のバージョンの Blazor では、要素をプロパティにバインドし、セッターを使用してプロパティの値を制御することで、双方向データ バインディングが実装されています。 .NET 7 以降の ASP.NET Core では、次の例で示すように、@bind:get
/@bind:set
修飾子構文を使用して双方向データ バインディングを実装します。
@bind:get
/@bind:set
を使用する次の "正しいアプローチ" を検討してください。
<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;
}
}
@bind:get
/@bind:set
修飾子を使用すると、@bind:set
を介した inputValue
の基になる値の制御と、@bind:get
を介した inputValue
の値の要素の値へのバインドの両方が行われます。 前の例は、双方向データ バインディングを実装するための正しい方法を示しています。
C# の get
および set
アクセサーを使用したプロパティへのバインド
次の DecimalBinding
コンポーネントで示すように、C# の get
および set
アクセサーを使用して、カスタム バインディング形式の動作を作成できます。 このコンポーネントでは、string
プロパティ (DecimalValue
) によって、最大 3 桁の小数点以下を含む正または負の 10 進数を <input>
要素にバインドします。
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);
}
}
}
}
Note
get
/set
アクセサーによるプロパティへの両方向のバインドでは、EventCallback.InvokeAsync によって返された Task を破棄する必要があります。 双方向データ バインディングでは、@bind:get
/@bind:set
修飾子を使うことをお勧めします。 詳しくは、この記事で前述した @bind:get
/@bind:set
のガイダンスを参照してください。
Note
get
/set
アクセサーによるプロパティへの両方向のバインドでは、EventCallback.InvokeAsync によって返された Task を破棄する必要があります。 .NET 7 以降の ASP.NET Core の双方向データ バインディングでは、この記事の 7.0 以降のバージョンで説明されている、@bind:get
/@bind:set
修飾子を使うことをお勧めします。
<select>
要素を使用した複数のオプションの選択
バインドでは、<select>
要素を使用した multiple
オプションの選択がサポートされます。 @onchange
イベントでは、イベント引数 (ChangeEventArgs
) を介して選択した要素の配列が提供されます。 その値は配列型にバインドする必要があります。
BindMultipleInput.razor
:
@page "/bind-multiple-input"
<h1>Bind Multiple <code>input</code>Example</h1>
<p>
<label>
Select one or more cars:
<select @onchange="SelectedCarsChanged" multiple>
<option value="audi">Audi</option>
<option value="jeep">Jeep</option>
<option value="opel">Opel</option>
<option value="saab">Saab</option>
<option value="volvo">Volvo</option>
</select>
</label>
</p>
<p>
Selected Cars: @string.Join(", ", SelectedCars)
</p>
<p>
<label>
Select one or more cities:
<select @bind="SelectedCities" multiple>
<option value="bal">Baltimore</option>
<option value="la">Los Angeles</option>
<option value="pdx">Portland</option>
<option value="sf">San Francisco</option>
<option value="sea">Seattle</option>
</select>
</label>
</p>
<span>
Selected Cities: @string.Join(", ", SelectedCities)
</span>
@code {
public string[] SelectedCars { get; set; } = new string[] { };
public string[] SelectedCities { get; set; } = new[] { "bal", "sea" };
private void SelectedCarsChanged(ChangeEventArgs e)
{
if (e.Value is not null)
{
SelectedCars = (string[])e.Value;
}
}
}
空の文字列と null
値がデータ バインディングでどのように処理されるかについては、「C# オブジェクトの null
値への <select>
要素オプションのバインド」セクションを参照してください。
C# オブジェクトの null
値への <select>
要素オプションのバインド
次の理由により、<select>
要素のオプション値を C# オブジェクトの null
値として表すよい方法はありません。
- HTML 属性には
null
値を設定できません。 HTML でnull
に最も近いのは、<option>
要素から HTML のvalue
属性を除去することです。 value
属性のない<option>
を選択すると、ブラウザーではその<option>
の要素の "テキスト コンテンツ" として値が扱われます。
Blazor フレームワークでは、次の処理が必要になるため、既定の動作の抑制は試みられません。
- フレームワークでの特殊なケースの回避策のチェーンの作成。
- 現在のフレームワークの動作に対する破壊的変更。
HTML で null
に最も近いと思われるものは、"空の文字列" の value
です。 Blazor フレームワークでは、<select>
の値への双方向バインドに対する空の文字列変換として null
が処理されます。
解析不可能値
ユーザーがデータ バインド要素に解析不可能な値を指定すると、バインド イベントがトリガーされると、解析不可能な値は自動的に以前の値に戻されます。
次のコンポーネントについて考えてみます。ここで、<input>
要素は、初期値が 123
である int
型にバインドされています。
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;
}
バインドは要素の onchange
イベントに適用されます。 ユーザーがテキスト ボックスのエントリの値を 123.45
に更新してフォーカスを変更した場合、onchange
が呼び出されると、要素の値は 123
に戻ります。 値 123.45
が拒否されて元の値 123
が設定された場合、ユーザーはその値が受け入れられなかったことを認識します。
oninput
イベント (@bind:event="oninput"
) の場合、解析できない値が入力されたキーストロークの後に、値は元に戻されます。 int
にバインドされた型の oninput
イベントを対象とする場合、ユーザーがドット (.
) 文字を入力することはできません。 ドット (.
) 文字はすぐに削除されるので、ユーザーは整数のみが許可されるというフィードバックをすぐに受け取ることができます。 oninput
イベントで値を元に戻すのが理想的ではないシナリオもあります。たとえば、ユーザーが解析できない <input>
値をクリアできる必要がある場合などです。 代替手段は次のとおりです。
oninput
イベントは使用しません。 既定のonchange
イベントを使用します。この場合、要素がフォーカスを失うまで無効な値は元に戻されません。int?
やstring
などの null 許容型にバインドし、@bind:get
/@bind:set
修飾子 (この記事で前述) を使用するか、無効なエントリを処理するカスタムのget
およびset
アクセサー ロジックを使用してプロパティにバインドします。- InputNumber<TValue> や InputDate<TValue> などの入力コンポーネントを、フォーム検証と共に使います。 入力コンポーネントとフォーム検証コンポーネントには、無効な入力を管理するためのサポートが組み込まれています。
- ユーザーの無効な入力を許可し、関連付けられた EditContext で検証エラーを受信します。
- ユーザーが追加の Web フォーム データを入力することを妨げることなく、UI に検証エラーを表示します。
書式指定文字列
データ バインディングでは、@bind:format="{FORMAT STRING}"
を使用して単一の DateTime 書式指定文字列を処理できます。ここで、{FORMAT STRING}
プレースホルダーは書式指定文字列です。 通貨や数値の形式など、その他の書式指定式は現時点では使用できませんが、今後のリリースで追加される可能性があります。
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);
}
上のコードでは、<input>
要素のフィールドの種類 (type
属性) は既定で text
に設定されています。
サポートされるのは、Null 許容の System.DateTime および System.DateTimeOffset だけです。
private DateTime? date;
private DateTimeOffset? dateOffset;
Blazor には日付の書式を設定するためのサポートが組み込まれているため、date
フィールド型の形式を指定することは推奨されません。 この推奨事項には反しますが、date
フィールド型で形式を指定する場合は、yyyy-MM-dd
日付形式を使用してバインドしたのみ正しく機能します。
<input type="date" @bind="startDate" @bind:format="yyyy-MM-dd">
コンポーネント パラメーターによるバインディング
一般的なシナリオは、子コンポーネントのプロパティをその親コンポーネントのプロパティにバインドすることです。 このシナリオは、複数のレベルのバインドが同時に発生するため、チェーン バインドと呼ばれます。
子コンポーネントには、@bind
構文を使用してチェーン バインドは実装することはできません。 子コンポーネントから親のプロパティを更新するには、イベント ハンドラーと値を別々に指定する必要があります。 子コンポーネントによるデータ バインディングを設定するため、親コンポーネントによって引き続き @bind
構文が活用されます。
次の ChildBind
コンポーネントには、Year
コンポーネント パラメーターと EventCallback<TValue> があります。 慣例により、パラメーターの EventCallback<TValue> には、コンポーネント パラメーター名としてサフィックス "Changed
" を使用して名前を付ける必要があります。 名前付け構文は {PARAMETER NAME}Changed
です。ここで、{PARAMETER NAME}
プレースホルダーはパラメーター名です。 次の例では、EventCallback<TValue> に YearChanged
という名前が付けられています。
EventCallback.InvokeAsync によって、バインディングに関連付けられているデリゲートが指定の引数で呼び出され、変更されたプロパティのイベント通知が送信されます。
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));
}
}
イベントと EventCallback<TValue> について詳しくは、記事「ASP.NET Core Blazor のイベント処理」の「EventCallback」セクションを参照してください。
次の Parent1
コンポーネントでは、year
フィールドが子コンポーネントの Year
パラメーターにバインドされています。 Year
パラメーターには、Year
パラメーターの型と一致するコンパニオン YearChanged
イベントがあるため、バインド可能です。
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);
}
}
コンポーネント パラメーター バインドは、@bind:after
イベントをトリガーすることもできます。 次の例では、Year
コンポーネント パラメーターをバインドした後、YearUpdated
メソッドが非同期的に実行されます。
<ChildBind @bind-Year="year" @bind-Year:after="YearUpdated" />
@code {
...
private async Task YearUpdated()
{
... = await ...;
}
}
慣例により、ハンドラーに割り当てられた @bind-{PROPERTY}:event
属性を含めることで、プロパティを対応するイベント ハンドラーにバインドできます。ここで、{PROPERTY}
プレースホルダーはプロパティです。 <ChildBind @bind-Year="year" />
は次のように書く場合と同じです。
<ChildBind @bind-Year="year" @bind-Year:event="YearChanged" />
もっと洗練された実世界の例では、次の PasswordEntry
コンポーネントにより、次のことが行われます。
password
フィールドに<input>
要素の値を設定します。- 子の
password
フィールドの現在の値を引数として渡すEventCallback
を使用して、Password
プロパティの変更を親コンポーネントに公開します。 onclick
イベントを使用して、ToggleShowPassword
メソッドをトリガーします。 詳細については、「ASP.NET Core Blazor のイベント処理」を参照してください。
警告
アプリ シークレット、接続文字列、資格情報、パスワード、個人識別番号 (PIN)、プライベート C#/.NET コード、秘密キー/トークンをクライアント側コードに格納しないでください。これは安全ではありません。 テスト/ステージング環境と運用環境では、サーバー側の Blazor コードと Web API は、プロジェクト コードまたは構成ファイル内で資格情報を維持しないように、セキュリティで保護された認証フローを使用する必要があります。 ローカル開発テスト以外では、環境変数が最も安全なアプローチではないため、環境変数を使用して機密データを格納しないようにすることをお勧めします。 ローカル開発テストでは、機密データをセキュリティで保護するために、 Secret Manager ツール をお勧めします。 詳細については、「 機密データと資格情報を安全に管理するを参照してください。
PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword() => showPassword = !showPassword;
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword() => showPassword = !showPassword;
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private async Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
await PasswordChanged.InvokeAsync(password);
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
PasswordEntry
コンポーネントは、次の 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
コンポーネントが最初にレンダリングされると、Not set
の password
値が UI に表示されます。 初期レンダリングの後、password
の値により、PasswordEntry
コンポーネントの Password
コンポーネント パラメーター値に加えられた変更が反映されます。
Note
前の例では、子 PasswordEntry
コンポーネントから親 PasswordBinding
コンポーネントにパスワードを一方向にバインドします。 このシナリオでは、パスワードを親に渡すだけのアプリで再利用するための共有パスワード入力コンポーネントをアプリに含めることが目標である場合、双方向バインディングは要件ではありません。 子コンポーネントのパラメーターに直接書き込むことなく双方向のバインディングを許可する方法については、 この記事の「3 つ以上のコンポーネント間でバインドする」セクションの NestedChild
コンポーネントの例を参照してください。
ハンドラーでチェックを実行するか、エラーをトラップします。 次の改訂された PasswordEntry
コンポーネントでは、パスワードの値にスペースが使用されている場合、すぐにユーザーにフィードバックが返されます。
PasswordEntry.razor
:
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
private string? validationMessage;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
if (password != null && password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword() => showPassword = !showPassword;
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
private string? validationMessage;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
if (password != null && password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword() => showPassword = !showPassword;
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
private string? validationMessage;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
if (password != null && password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
次の例では、Password
コンポーネント パラメーターをバインドした後、PasswordUpdated
メソッドが非同期的に実行されます。
<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="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string? password;
private string? validationMessage;
[Parameter]
public string? Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e?.Value?.ToString();
if (password != null && password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
private string validationMessage;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
if (password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
<div class="card bg-light mt-3" style="width:22rem ">
<div class="card-body">
<h3 class="card-title">Password Component</h3>
<p class="card-text">
<label>
Password:
<input @oninput="OnPasswordChanged"
required
type="@(showPassword ? "text" : "password")"
value="@password" />
</label>
<span class="text-danger">@validationMessage</span>
</p>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
</div>
</div>
@code {
private bool showPassword;
private string password;
private string validationMessage;
[Parameter]
public string Password { get; set; }
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
private Task OnPasswordChanged(ChangeEventArgs e)
{
password = e.Value.ToString();
if (password.Contains(' '))
{
validationMessage = "Spaces not allowed!";
return Task.CompletedTask;
}
else
{
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
}
}
private void ToggleShowPassword()
{
showPassword = !showPassword;
}
}
3 つ以上のコンポーネント間でバインドする
入れ子になった任意の数のコンポーネントを介してパラメーターをバインドできますが、次のような一方向のデータ フローを考慮する必要があります。
- 変更通知は "階層をフローアップ" します。
- 新しいパラメーター値は "階層をフローダウン" します。
次の例に示すように、一般的な推奨される方法は、基になるデータを親コンポーネントに格納のみすることであり、これにより、更新する必要がある状態に関する混乱を避けることができます。
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}";
}
}
次の NestedChild
コンポーネントでは、NestedGrandchild
コンポーネントは:
ChildMessage
の値を@bind:get
構文でGrandchildMessage
に割り当てます。ChildMessageChanged
が@bind:set
構文を使用して実行された場合にGrandchildMessage
を更新します。
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}");
}
}
警告
通常は、独自のコンポーネント パラメーターに直接書き込むコンポーネントは作成しないでください。 上記の NestedChild
コンポーネントでは、ChildMessage
パラメーターに直接書き込むのではなく、BoundValue
プロパティが使用されます。 詳細については、「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}");
}
}
メモリ内で、および必ずしも入れ子にはなっていないコンポーネント間でデータを共有するのに適した別の方法については、「ASP.NET Core Blazor の状態管理」を参照してください。
バインドされたフィールドまたはプロパティの式ツリー
バインドとのより深い対話を容易にするために、Blazor では、バインドされたフィールドまたはプロパティの式ツリーをキャプチャできます。 これを行うには、フィールド名またはプロパティ名に Expression
というサフィックスを付けたプロパティを定義します。 {FIELD OR PROPERTY NAME}
という名前のフィールドまたはプロパティの場合、対応する式ツリーのプロパティ名は {FIELD OR PROPERTY NAME}Expression
です。
次の ChildParameterExpression
コンポーネントは、Year
式のモデルとフィールド名を識別します。 モデルとフィールド名を取得するために使用される FieldIdentifier は、編集できる 1 つのフィールドを一意に識別します。 これは、モデル オブジェクトのプロパティに対応したものにすることも、他の任意の名前付きの値にすることもできます。 パラメーターの式の使用は、カスタム検証コンポーネントを作成する場合に便利です。これは、Microsoft Blazor のドキュメントでは説明されていませんが、多数のサード パーティのリソースに記載されています。
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;
}
その他のリソース
ASP.NET Core