大家好,我是Edison。
最近在学习Blazor做全栈开发,因此根据老习惯,我会将我的学习过程记录下来,一来体系化整理,二来作为笔记供将来翻看。
本篇,我们来了解下在Blazor中数据是如何绑定的。
关于数据绑定
如果希望 HTML 元素显示值,可以编写代码来更改显示内容。如果值发生更改,则需要编写额外的代码以更新显示内容。
在 Blazor 中,可以使用数据绑定将 HTML 元素连接到字段、属性或表达式。
这样,当值发生更改时,HTML 元素便会自动更新。更新通常在更改后迅速发生,并且我们无需编写任何更新代码。
例如,我们使用@bind指令完成当变量被更改时,h1和input标签的值也同步更新:
@page "/"
<h1>My favorite pizza is: @favPizza</h1>
Enter your favorite pizza:
<input @bind="favPizza" />
@code {
private string favPizza { get; set; } = "Margherita"
@bind指令比较智能,它大概可以知道你需要绑定标签的哪个属性,例如:将其绑定到input标签时,它会绑定value属性。而将其绑定到checkbox中,它则自动绑定checked属性。
将元素绑定到特定事件
默认情况下,@bind指令对于input控件通常会绑定到DOM onchange事件。对于上面的例子来说,当在文本框中输入了数据时,只有当离开文本框或选择按下Enter键或者Tab键,才会触发DOM onchange事件让h1标签的内容发生改变。
假设,我们希望在文本框中输入任何内容时,都会触发h1标签内容的更改。这个事件就不再是DOM onchange事件了而是DOM oninput事件,因此,我们可以借助 @bind-value 和 @bind-value:event 指令来绑定到oninput事件:
@page "/"
<h1>My favorite pizza is: @favPizza</h1>
Enter your favorite pizza:
<input @bind-value="favPizza" @bind-value:event="oninput" />
@code {
private string favPizza { get; set; } = "Margherita"
实现效果:
设置绑定值的格式
在很多场景中,我们可能需要对日期进行本地化的格式转换。这里,我们就可以借助@bind:format指令来指定格式:
@page "/ukbirthdaypizza"
<h1>Order a pizza for your birthday!</h1>
Enter your birth date:
<input @bind="birthdate" @bind:format="dd-MM-yyyy" />
@code {
private DateTime birthdate { get; set; } = new(2000, 1, 1);
此外,我们也可以采用属性的get/set访问器来实现自定义的格式转换,例如下面的示例:
@page "/pizzaapproval"
@using System.Globalization
<h1>Pizza: @PizzaName</h1>
<p>Approval rating: @approvalRating</p>
<label>
Set a new approval rating:
<input @bind="ApprovalRating" />
</label>
@code {
private decimal approvalRating = 1.0;
private NumberStyles style = NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign;
private CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
private string ApprovalRating
get => approvalRating.ToString("0.000", culture);
if (Decimal.TryParse(value, style, culture, out var number))
approvalRating = Math.Round(number, 3);
组件参数的绑定(双向绑定)
在有些场景中,父组件中嵌套了子组件,我们希望父组件中的变化能够同步更新到子组件,同理,子组件中的变化能够同步更新父组件中。实现的方式就是通过组件参数(Parameter),而这个场景也被称之为链式绑定(Chained Bind)。
在Blazor中,我们可以通过 @bind-{PROPERTY} 指令来实现链式绑定,其中的 {PROPERTY} 占位符表示要绑定的属性名字。
例如,我们有以下两个组件,Parent-1.razor是父组件,其中嵌套了 ChildBind.razor 这个子组件。
ChindBind.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
<button @onclick="UpdateYearFromChild">Update Year from Child</button>
@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));
Parent-1.razor:
@page "/parent-1"
<h1>Parent Component</h1>
<p>Parent <code>year</code>: @year</p>
<button @onclick="UpdateYear">Update Parent <code>year</code></button>
<ChildBind @bind-Year="year" />
@code {
private Random r = new();
private int year = 1979;
private void UpdateYear()
year = r.Next(1950, 2021);
可以看到,这里Parent-1.razor中通过@bind-Year指令与子组件的Year属性进行了绑定。
需要注意的是,通常情况下,我们还需要设置一个@bing-Year:event指令,不过由于我们定义的事件回调的名字YearChanged是符合自动匹配的,即命名格式是 {PARAMETER NAME}Changed,也就可以省略@bind-Year:event="YearChanged"这个设置,这就是所谓的“约定大于配置”。因此,它其实等价于:
<ChildBind @bind-Year="year" @bind-Year:event="YearChanged" />
因此,我们可以知道,只需要在HTML属性中加上@bind-{PROPERTY}指令,就是告诉Blazor不仅要将更改到推送到组件,还要观察组件的任何修改并及时更新自己的状态。通常来说,这种在父组件和子组件之间的数据绑定 也叫做 双向绑定。
同时,我们也注意到在Blazor中事件回调(委托)的统一类型为:EventCallback。我们在子组件中使用的是InvokeAsync()方法也说明它是线程安全的。
实现效果:
在一个更真实常见的场景中,我们可能希望实现数据实施修改的联动更新,类似于下面的例子。
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>
<button class="btn btn-primary" @onclick="ToggleShowPassword">
Show password
</button>
@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;
validationMessage = string.Empty;
return PasswordChanged.InvokeAsync(password);
private void ToggleShowPassword()
showPassword = !showPassword;
PasswordBinding.razor:
@page "/password-binding"
<h1>Password Binding</h1>
<PasswordEntry @bind-Password="password" />
<code>password</code>: @password
@code {
private string password = "Not set";
最终效果:
组件参数绑定的最佳实践
我们可以在多层嵌套的组建中绑定组件参数,但是我们必须遵循这类单向数据绑定的流程:
一个推荐的方式是只在父组件中存储源数据,以此避免在状态需要更新时容易产生的混淆。
例如,下面这个例子:
Parent2.razor:
@page "/parent-2"
<h1>Parent Component</h1>
<p>Parent Message: <b>@parentMessage</b></p>
<button @onclick="ChangeValue">Change from Parent</button>
<NestedChild @bind-ChildMessage="parentMessage" />
@code {
private string parentMessage = "Initial value set in Parent";
private void ChangeValue()
parentMessage = $"Set in Parent {DateTime.Now}";
NestedChild.razor:
<div class="border rounded m-1 p-1">
<h2>Child Component</h2>
<p>Child Message: <b>@ChildMessage</b></p>
<button @onclick="ChangeValue">Change from Child</button>
<NestedGrandchild @bind-GrandchildMessage="BoundValue" />
@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}");
NestedGrandchild.razor:
<div class="border rounded m-1 p-1">
<h3>Grandchild Component</h3>
<p>Grandchild Message: <b>@GrandchildMessage</b></p>
<button @onclick="ChangeValue">Change from Grandchild</button>
@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}");
从示例中可以看出,它遵循了两个原则:
(1)源数据是自顶向下流动,即parentMessage 和 BoundValue 两个值。
(2)事件通知是自底向上流动,即子组件的ChangeValue方法都会调用EventCallback来向上通知。
最终效果:
本篇,我们了解了数据如何在Blazor中进行数据的绑定。
下一篇,我们学习一下在Blazor中数据绑定的各种花样。
Microsoft Docs,《与Blazor Web应用中的数据交互》
Microsoft Docs,《Blazor数据绑定》
年终总结:Edison的2021年终总结
数字化转型:我在传统企业做数字化转型
C#刷题:C#刷剑指Offer算法题系列文章目录
.NET面试:.NET开发面试知识体系
.NET大会:2020年中国.NET开发者大会PDF资料
您也可以在 Blazor 中实现复杂对象的双向绑定,例如表单输入与数据模型。指令将组件的属性与用户界面元素的属性进行绑定。:您还可以在自定义组件中使用双向绑定。字符串与文本框的值双向绑定。当用户更改文本框的值时,对象的属性进行双向绑定,以便在表单提交时,组件的属性进行双向绑定。将自动更新,反之亦然。对象中的属性会更新。
⒈ 单向绑定
在razor 模版中, 直接使用 @variable, 其中variable 为 C#属性或类字段, 这样的写法是单向绑定, 即按照C#字段变化后, DOM上的内容也会更新.
@page "/bind"
<p>class field @field </p>
<p> class property @MyProperty </p>
原文链接:https://blazor-university.com/components/two-way-binding/双向绑定源代码[1]注意: 如果您还没有这样做过,请在继续本节之前先执行单向绑定[2]中的步骤。到目前为止,我们有一个包含嵌入组件的页面,并且我们组件的部分状态是从其宿主视图(Counter 页面)以名为 CurrentCounterValue 的参...
数据绑定就是前端显示(页面元素的值)与后端数据(C#代码)是联动的。可以是单向的(后端影响前端)也可以是双向的(前后端相互影响)。其中,@变量名 是引用后端变量,razor语法。可见,前端输入内容,后端直接发生变化了。后端code中改变了,前端就会受影响。前端输入什么值,后端都不会发生变化。
bind将表达式的当前值与 value 特性关联,并使用注册的处理程序处理更改。这个针对的是一般的html组件。@bind-Value针对的是一般的Blazor内置的组件,要结合。
Blazor自定义Input使用@bind-Value,必须要定义ValueChanged属性
public class MyInput : ComponentBase {
[Parameter] public string Value { get; set; }
[Parameter] public EventCallback<string> ValueChanged { get; set; }
在我使用Blazor.net的一项作业中,很少有表单具有输入控件作为日期选择器日历。根据业务逻辑的自定义需求不可能使用EditForm中的当前Blazor InputDate组件实现。下面列出了一些限制使用默认组件的逻辑:
限制日期,如开始日期和结束日期,或者限制未来或过去的日期
输入字段格式
表单验证是任何系统都会存在的功能。如果你是使用 Blazor 中内置的验证组件,你需要提前掌握 System.ComponentModel.DataAnnotations 验证注解特性的用法。
翻译自 Waqas Anwar 2021年3月21日的文章《A Developer’s Guide to Blazor Data Binding》[1]现如今,大多数 Web 应用程序要么是在页面上显示某种数据,要么是使用表单从用户那里收集数据。这意味着每个 SPA 框架都必须支持数据绑定,以便开发者可以将数据与label、form控件等元素进行绑定。Blazor...