添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

本文介绍 Blazor 应用中 Razor 组件和文档对象模型 (DOM) 元素的数据绑定功能。

Razor 组件使用 @bind Razor 指令特性提供与字段、属性或 Razor 表达式值的数据绑定功能。

下面的示例执行以下绑定:

  • <input> 元素值绑定到 C# inputValue 字段。
  • 将第二个 <input> 元素值绑定到 C# InputValue 属性。
  • 当一个 <input> 元素失去焦点时,将更新其绑定字段或属性。

    Pages/Bind.razor :

    @page "/bind" <input @bind="inputValue" /> <input @bind="InputValue" /> <li><code>inputValue</code>: @inputValue</li> <li><code>InputValue</code>: @InputValue</li> @code { private string? inputValue; private string? InputValue { get; set; }

    仅当呈现组件时(而不是响应字段或属性值更改),才会在 UI 中更新文本框。 由于组件会在事件处理程序代码执行之后呈现自己,因此在触发事件处理程序之后,通常会在 UI 中反映字段和属性更新。

    下面的示例将 InputValue 属性绑定到第二个 <input> 元素的 value onchange 特性 ( change ),演示了如何使用 HTML 撰写数据绑定。 下面示例中的第二个 <input> 元素旨在演示概念,并非用于建议在 Razor 中绑定数据的方式。

    Pages/BindTheory.razor :

    @page "/bind-theory" <label> Normal Blazor binding: <input @bind="InputValue" /> </label> <label> Demonstration of equivalent HTML binding: <input value="@InputValue" @onchange="@((ChangeEventArgs __e) => InputValue = __e?.Value?.ToString())" /> </label> <code>InputValue</code>: @InputValue @code { private string? InputValue { get; set; } Demonstration of equivalent HTML binding: <input value="@InputValue" @onchange="@((ChangeEventArgs __e) => InputValue = __e?.Value?.ToString())" /> </label> <code>InputValue</code>: @InputValue @code { private string? InputValue { get; set; } Demonstration of equivalent HTML binding: <input value="@InputValue" @onchange="@((ChangeEventArgs __e) => InputValue = __e.Value.ToString())" /> </label> <code>InputValue</code>: @InputValue @code { private string InputValue { get; set; } Demonstration of equivalent HTML binding: <input value="@InputValue" @onchange="@((ChangeEventArgs __e) => InputValue = __e.Value.ToString())" /> </label> <code>InputValue</code>: @InputValue @code { private string InputValue { get; set; }

    BindTheory 组件呈现时,HTML 演示 <input> 元素的 value 来自 InputValue 属性。 用户在文本框中输入值并更改元素焦点时,会触发 onchange 事件并将 InputValue 属性设置为更改的值。 实际上,代码执行更加复杂,因为 @bind 会处理执行类型转换的情况。 通常, @bind 将表达式的当前值与 <input> value 特性关联,并使用注册的处理程序处理更改。

    通过包括 @bind:event="{EVENT}" 特性(用 DOM 事件表示 {EVENT} 占位符),可在其他 文档对象模型 (DOM) 事件发生时绑定属性或字段。 下面的示例在 <input> 元素的 oninput 事件 ( input ) 触发时将 InputValue 属性绑定到该元素的值。 与在元素失去焦点时触发的 onchange 事件 ( change ) 不同, oninput ( input ) 在文本框的值发生更改时触发。

    Page/BindEvent.razor :

    @page "/bind-event" <input @bind="InputValue" @bind:event="oninput" /> <code>InputValue</code>: @InputValue @code { private string? InputValue { get; set; }

    若要在绑定后执行异步逻辑,请将 @bind:after="{EVENT}" {EVENT} 占位符的 DOM 事件配合使用。 在同步分配绑定值之前,不会执行分配的 C# 方法。

    不支持结合使用事件回调参数 ( [Parameter] public EventCallback<string> ValueChanged { get; set; } ) 和 @bind:after 。 相反,将返回 Action Task 的方法传递给 @bind:after

    如下示例中:

  • <input> 元素的 value 同步绑定到 searchText 的值。
  • 每次在字段中击键( onchange 事件)后,将异步执行 PerformSearch 方法。
  • PerformSearch 使用异步方法 ( FetchAsync ) 调用服务以返回搜索结果。
  • @inject ISearchService SearchService
    <input @bind="searchText" @bind:after="PerformSearch" />
    @code {
        private string? searchText;
        private string[]? searchResult;
        private async Task PerformSearch()
            searchResult = await SearchService.FetchAsync(searchText);
    

    Pages/BindAfter.razor:

    @page "/bind-after"
    @using Microsoft.AspNetCore.Components.Forms
    <h1>Bind After Examples</h1>
    <h2>Elements</h2>
    <input type="text" @bind="text" @bind:after="() => { }" />
    <input type="text" @bind="text" @bind:after="After" />
    <input type="text" @bind="text" @bind:after="AfterAsync" />
    <h2>Components</h2>
    <InputText @bind-Value="text" @bind-Value:after="() => { }" />
    <InputText @bind-Value="text" @bind-Value:after="After" />
    <InputText @bind-Value="text" @bind-Value:after="AfterAsync" />
    @code {
        private string text = "";
        private void After() {}
        private Task AfterAsync() { return Task.CompletedTask; }
    

    有关 InputText 组件的详细信息,请参阅 ASP.NET Core Blazor 窗体和输入组件

    组件通过定义一对参数来支持双向数据绑定:

  • @bind:get:指定要绑定的值。
  • @bind:set:指定值更改时的回调。
  • @bind:get@bind:set 修饰符始终一起使用。

    不支持结合使用事件回调参数和 @bind:set ([Parameter] public EventCallback<string> ValueChanged { get; set; })。 相反,将返回 ActionTask 的方法传递给 @bind:set

    Pages/BindGetSet.razor:

    @page "/bind-get-set"
    @using Microsoft.AspNetCore.Components.Forms
    <h1>Bind Get Set Examples</h1>
    <h2>Elements</h2>
    <input type="text" @bind:get="text" @bind:set="(value) => { text = value; }" />
    <input type="text" @bind:get="text" @bind:set="Set" />
    <input type="text" @bind:get="text" @bind:set="SetAsync" />
    <h2>Components</h2>
    <InputText @bind-Value:get="text" @bind-Value:set="(value) => { text = value; }" />
    <InputText @bind-Value:get="text" @bind-Value:set="Set" />
    <InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />
    @code {
        private string text = "";
        private void Set(string value)
            text = value;
        private Task SetAsync(string value)
            text = value;
            return Task.CompletedTask;
    

    有关 InputText 组件的详细信息,请参阅 ASP.NET Core Blazor 窗体和输入组件

    有关使用 @bind:get@bind:set 的另一个示例,请参阅本文后面绑定到两个以上的组件部分。

    Razor 属性绑定区分大小写:

  • @bind@bind:event@bind:after 有效。
  • @Bind/@bind:Event/@bind:aftEr(大写字母)或 @BIND/@BIND:EVENT/@BIND:AFTER(全部是大写字母)无效
  • 使用 @bind:get/@bind:set 修饰符并避免使用事件处理程序进行双向数据绑定

    双向数据绑定是不能使用事件处理程序实现的。 需要使用 @bind:get/@bind:set 修饰符进行双向数据绑定。

    来看看下面使用事件处理程序进行双向数据绑定的一种失效性方法:

    <input value="@inputValue" @oninput="OnInput" /> <code>inputValue</code>: @inputValue @code { private string? inputValue; private void OnInput(ChangeEventArgs args) var newValue = args.Value?.ToString() ?? string.Empty; inputValue = newValue.Length > 4 ? "Long!" : newValue;

    在提供第四个字符后,OnInput 事件处理程序将 inputValue 的值更新为 Long!。 但是,用户可以继续在 UI 中向元素值添加字符。 inputValue 的值并没有随着每次击键而绑定回元素的值。 前面的示例只能进行单向数据绑定。

    此行为的原因是,Blazor 不知道代码打算在事件处理程序中修改 inputValue 的值。 Blazor 不会尝试强制文档对象模型 (DOM) 元素值和 .NET 变量值进行匹配,除非它们通过 @bind 语法绑定。 在 Blazor 的早期版本中,双向数据绑定是通过将元素绑定到属性并使用其资源库控制属性的值实现的。 在 ASP.NET Core 7.0 或更高版本中,@bind:get/@bind:set 修饰符语法用于实现双向数据绑定,如下一示例所示。

    来看看下面使用 @bind:get/@bind:set 进行双向数据绑定的正确方法:

    <input @bind:event="oninput" @bind:get="inputValue" @bind:set="OnInput" /> <code>inputValue</code>: @inputValue @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:getinputValue 的值绑定到元素的值。 前面的示例演示了实现双向数据绑定的正确方法。

    使用 C# getset 访问器绑定到属性

    C# getset 访问器可用于创建自定义绑定格式行为,如下面的 DecimalBinding 组件所示。 此组件通过 string 属性 (DecimalValue) 将最多具有三个小数位的正或负小数伪绑定到 <input> 元素。

    Pages/DecimalBinding.razor:

    @page "/decimal-binding" @using System.Globalization <label> Decimal value (±0.000 format): <input @bind="DecimalValue" /> </label> <code>decimalValue</code>: @decimalValue @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); if (Decimal.TryParse(value, style, culture, out var number)) decimalValue = Math.Round(number, 3); 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); if (Decimal.TryParse(value, style, culture, out var number)) decimalValue = Math.Round(number, 3); 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); if (Decimal.TryParse(value, style, culture, out var number)) decimalValue = Math.Round(number, 3); 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); if (Decimal.TryParse(value, style, culture, out var number)) decimalValue = Math.Round(number, 3);

    使用 <select> 元素的多个选项选择

    绑定支持使用 <select> 元素的 multiple 选项选择。 @onchange 事件通过事件参数 (ChangeEventArgs) 提供一个选定元素的数组。 值必须绑定到数组类型。

    Pages/BindMultipleInput.razor:

    @page "/bind-multiple-input"
    <h1>Bind Multiple <code>input</code>Example</h1>
        <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>
        Selected Cars: @string.Join(", ", SelectedCars)
        <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>
        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 值的信息,请参阅<select> 元素绑定到 C# 对象 null部分。

    <select> 元素选项绑定到 C# 对象 null

    由于以下原因,没有将 <select> 元素选项值表示为 C# 对象 null 值的合理方法:

  • HTML 属性不能具有 null 值。 HTML 中最接近的 null 等效项是 <option> 元素中缺少 HTML value 属性。
  • 选择没有 value 属性的 <option> 时,浏览器会将值视为该 <option> 的元素的 文本内容。
  • Blazor 框架不会尝试取消默认行为,因为这会涉及以下操作:

  • 在框架中创建一系列特殊的解决办法。
  • 对当前框架行为进行重大更改。
  • HTML 中最合理的 null 等效项是空字符串value。 Blazor 框架处理 null 到空字符串之间的转换,以便双向绑定到 <select> 的值。

    无法分析的值

    如果用户向数据绑定元素提供无法分析的值,则在触发绑定事件时,无法分析的值会自动还原为以前的值。

    以下面的组件为例,其中 <input> 元素绑定到初始值为 123int 类型。

    Pages/UnparsableValues.razor:

    @page "/unparseable-values" <input @bind="inputValue" /> <code>inputValue</code>: @inputValue @code { private int inputValue = 123;

    默认情况下,绑定适用于元素的 onchange 事件。 如果用户将文本框输入的值更新为 123.45 并更改焦点,则元素的值将在 onchange 触发时还原为 123。 如果拒绝值 123.45 以采用原始值 123,则用户会了解其值不被接受。

    对于 oninput 事件 (@bind:event="oninput"),将在执行生成无法分析的值的击键操作后还原值。 当使用 int 绑定类型以 oninput 事件为目标时,会阻止用户键入小数点 (.) 字符。 小数点 (.) 字符会立即被删除,因此用户会收到仅允许整数的即时反馈。 在某些情况下,在 oninput 事件中还原值并不理想,例如在应该允许用户清除无法解析的 <input> 值时。 替代方案包括:

  • 不使用 oninput 事件。 使用默认 onchange 事件,其中无效值在元素失去焦点之前不会还原。
  • 绑定到一个可以为 null 的类型(如 int?string),并使用 @bind:get/@bind:set 修饰符(如本文前面所述)或使用自定义 getset 访问器逻辑绑定到属性来处理无效输入。
  • 使用窗体验证组件,如 InputNumber<TValue>InputDate<TValue>。 窗体验证组件提供用于管理无效输入的内置支持。 窗体验证组件:
  •