Blazor学习之旅(5)数据绑定
大家好,我是Edison。
本篇,我们来了解下在Blazor中数据是如何绑定的。
关于数据绑定
如果希望 HTML 元素显示值,可以编写代码来更改显示内容。如果值发生更改,则需要编写额外的代码以更新显示内容。
在 Blazor 中,可以使用数据绑定将 HTML 元素连接到字段、属性或表达式。
这样,当值发生更改时,HTML 元素便会自动更新。更新通常在更改后迅速发生,并且我们无需编写任何更新代码。
例如,我们使用@bind指令完成当变量被更改时,h1和input标签的值也同步更新:
@page "/" <h1>My favorite pizza is: @favPizza</h1> <p>
Enter your favorite pizza:
<input @bind="favPizza" />
</p> @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> <p>
Enter your favorite pizza:
<input @bind-value="favPizza" @bind-value:event="oninput" />
</p> @code {
private string favPizza { get; set; } = "Margherita"
}
实现效果:
设置绑定值的格式
在很多场景中,我们可能需要对日期进行本地化的格式转换。这里,我们就可以借助@bind:format指令来指定格式:
@page "/ukbirthdaypizza" <h1>Order a pizza for your birthday!</h1> <p>
Enter your birth date:
<input @bind="birthdate" @bind:format="dd-MM-yyyy" />
</p> @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> <p>
<label>
Set a new approval rating:
<input @bind="ApprovalRating" />
</label>
</p> @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);
set
{
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
</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));
}
}
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>
</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;
}
}
PasswordBinding.razor:
@page "/password-binding" <h1>Password Binding</h1> <PasswordEntry @bind-Password="password" /> <p>
<code>password</code>: @password
</p> @code {
private string password = "Not set";
}
最终效果:
组件参数绑定的最佳实践
我们可以在多层嵌套的组建中绑定组件参数,但是我们必须遵循这类单向数据绑定的流程:
更改通知是逐级向上流动
新的参数值是逐级向下流动
一个推荐的方式是只在父组件中存储源数据,以此避免在状态需要更新时容易产生的混淆。
例如,下面这个例子:
Parent2.razor:
@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.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="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}");
}
}
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}");
}
}
从示例中可以看出,它遵循了两个原则:
(1)源数据是自顶向下流动,即parentMessage 和 BoundValue 两个值。
(2)事件通知是自底向上流动,即子组件的ChangeValue方法都会调用EventCallback来向上通知。
最终效果:
小结
本篇,我们了解了数据如何在Blazor中进行数据的绑定。
下一篇,我们学习一下在Blazor中数据绑定的各种花样。
参考资料
Microsoft Docs,《与Blazor Web应用中的数据交互》
Microsoft Docs,《Blazor数据绑定》
Blazor学习之旅(5)数据绑定的更多相关文章
- 我的AngularJS 学习之旅
我的AngularJS 学习之旅 基础篇 1.Angular的 起源 2.比较Web 页面实现的几种方式 3.一些基本术语 4.Angular与其他框架的兼容性 5.总结 6.综合实例 很早之前就 ...
- AngularJS学习之旅
开篇 最近由于项目上可能需要用到AngularJS,公司将技术学习.调研的任务安排了下来,因此开始了我的AngularJS学习之路. 在这之前没写过技术博客,主要是由于太懒,另外自愧文笔不好,因此一直 ...
- [推荐]大量 Blazor 学习资源(二)
继上一篇<[推荐]大量 Blazor 学习资源(一)>之后,社区反应不错,但因个人原因导致这篇文章姗姗来迟,不过最终还是来了!这篇文章主要收集一些常用组件.书籍和电子书. 资料来源:htt ...
- WCF学习之旅—第三个示例之四(三十)
上接WCF学习之旅—第三个示例之一(二十七) WCF学习之旅—第三个示例之二(二十八) WCF学习之旅—第三个示例之三(二十九) ...
- Hadoop学习之旅二:HDFS
本文基于Hadoop1.X 概述 分布式文件系统主要用来解决如下几个问题: 读写大文件 加速运算 对于某些体积巨大的文件,比如其大小超过了计算机文件系统所能存放的最大限制或者是其大小甚至超过了计算机整 ...
- WCF学习之旅—第三个示例之二(二十八)
上接WCF学习之旅—第三个示例之一(二十七) 五.在项目BookMgr.Model创建实体类数据 第一步,安装Entity Framework 1) 使用NuGet下载最新版的Entity Fram ...
- WCF学习之旅—第三个示例之三(二十九)
上接WCF学习之旅—第三个示例之一(二十七) WCF学习之旅—第三个示例之二(二十八) 在上一篇文章中我们创建了实体对象与接口协定,在这一篇文章中我们来学习如何创建WCF的服务端代码.具体步骤见下面. ...
- WCF学习之旅—WCF服务部署到IIS7.5(九)
上接 WCF学习之旅—WCF寄宿前的准备(八) 四.WCF服务部署到IIS7.5 我们把WCF寄宿在IIS之上,在IIS中宿主一个服务的主要优点是在发生客户端请求时宿主进程会被自动启动,并且你可以 ...
- WCF学习之旅—WCF服务部署到应用程序(十)
上接 WCF学习之旅—WCF寄宿前的准备(八) WCF学习之旅—WCF服务部署到IIS7.5(九) 五.控制台应用程序宿主 (1) 在解决方案下新建控制台输出项目 ConsoleHosting.如下 ...
- WCF学习之旅—WCF服务的Windows 服务程序寄宿(十一)
上接 WCF学习之旅—WCF服务部署到IIS7.5(九) WCF学习之旅—WCF服务部署到应用程序(十) 七 WCF服务的Windows 服务程序寄宿 这种方式的服务寄宿,和IIS一样有一个一样 ...
随机推荐
- 【开源推荐】AI Interviewer:基于Spring-Alibaba-AI的智能面试官系统(附GitHub实战教程)
项目背景 作为开发者,你是否经历过: 技术面试时因紧张发挥失常? 刷了1000+LeetCode却不会表达解题思路? 花费上千元购买模拟面试服务? 今天推荐的AI Interviewer开源项目,正是 ...
- 图像处理中的 Gaussina Blur 和 SIFT 算法
Gaussina Blur 高斯模糊 高斯模糊的数学定义 高斯模糊是通过 高斯核(Gaussian Kernel) 对图像进行卷积操作实现的. 二维高斯函数定义为 \[G(x, y, \sigma) ...
- 修显示器led屏幕能亮但是显示异常
用电吹风热风大风 对着显示器的 ' led 区域 ' 吹十分钟 吹显示器线插口 电源线 插口 机箱 断电吹 // 温度挺高 还得吹显卡接口 线也要换新的 插口需要用线的接口 打磨金属 ...
- 康谋方案 | 康谋BRICK2与车载以太网设备轻松集成
导读:在当下,汽车行业在安全性.舒适性.智能和万物互联等方面彻底改变了传统车辆的定义.随着这一趋势,汽车行业逐渐开始采用车载以太网来进行车内数据通讯,比如100Base-T1.1000Base-T1, ...
- EF Core 中避免 SQL 注入的三种写法
SQL 注入攻击可能会对我们的应用程序产生严重影响,导致敏感数据泄露.未经授权的访问和应用程序受损.EF Core 提供了三种内置机制来防止 SQL 注入攻击. 1.利用 LINQ 查询语法和参数化查 ...
- SaltStack 远程命令执行中文乱码问题
问题 我在一台服务器上写了一个简单的 Python 脚本 haha.py,内容如下: [root@localhost ~]# cat haha.py print("你好") 当我在 ...
- TCP连接(Netty)
启动类增加 public static void main(String[] args) { SpringApplication application = new SpringApplication ...
- <HarmonyOS第一课01>HarmonyOS介绍
视频链接: https://developer.huawei.com/consumer/cn/training/course/slightMooc/C101717496599725471?ha_sou ...
- 第一章 Kafka 配置部署及SASL_PLAINTEXT安全认证
系列文章目录 第一章 Kafka 配置部署及SASL_PLAINTEXT安全认证 第二章 Spring Boot 整合 Kafka消息队列 生产者 第三章 Spring Boot 整合 Kafka ...
- 时间工具之“js初始化当前时间数据”
⑨前端:初始化当前时间数据 方案一(峰哥认可) // 2023-02this.$moment().format('yyyy-MM'),// 2023-02-02this.$moment().form ...