【Blazor】| 总结/Edison Zhou


大家好,我是Edison。

最近在学习Blazor做全栈开发,因此根据老习惯,我会将我的学习过程记录下来,一来体系化整理,二来作为笔记供将来翻看。

本篇,我们来了解下在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"requiredtype="@(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数据绑定》

年终总结:Edison的2021年终总结

数字化转型:我在传统企业做数字化转型

C#刷题:C#刷剑指Offer算法题系列文章目录

.NET面试:.NET开发面试知识体系

.NET大会:2020年中国.NET开发者大会PDF资料

Blazor学习之旅(5)数据绑定相关推荐

  1. Blazor学习之旅 (8) MudBlazor组件库介绍

    [Blazor]| 总结/Edison Zhou 大家好,我是Edison. 为了实现一个Web应用系统,需要有个看起来不丑的UI,而对于.NET程序员来说要做全栈开发还是有点难,而本篇介绍的这个UI ...

  2. [推荐]大量 Blazor 学习资源(二)

    继上一篇<[推荐]大量 Blazor 学习资源(一)>之后,社区反应不错,但因个人原因导致这篇文章姗姗来迟,不过最终还是来了!这篇文章主要收集一些常用组件.书籍和电子书. 资料来源:htt ...

  3. hadoop学习之旅1

    大数据介绍 大数据本质也是数据,但是又有了新的特征,包括数据来源广.数据格式多样化(结构化数据.非结构化数据.Excel文件.文本文件等).数据量大(最少也是TB级别的.甚至可能是PB级别).数据增长 ...

  4. 基于设计模式的学习之旅-----访问者模式(附源码)

    基于设计模式的学习之旅-----访问者模式 1.初始访问者模式 2.什么是访问者模式 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 3.模 ...

  5. WCF学习之旅—WCF服务的WAS寄宿(十二)

    上接    WCF学习之旅-WCF服务部署到IIS7.5(九) WCF学习之旅-WCF服务部署到应用程序(十) WCF学习之旅-WCF服务的Windows 服务程序寄宿(十一) 八.WAS宿主 IIS ...

  6. 基于 Android NDK 的学习之旅-----资源释放

    基于 Android NDK 的学习之旅-----资源释放 做上一个项目的时候因为与C引擎交互频繁,有时候会突然莫名其妙的的整个应用程序直接挂掉.因为我是学Java 开始的,所以对主动释放内存没多大概 ...

  7. java message bus_【Microsoft Azure学习之旅】消息服务Service Bus的学习笔记及Demo示例...

    今年项目组做的是Cloud产品,有幸接触到了云计算的知识,也了解并使用了当今流行的云计算平台Amazon AWS与Microsoft Azure.我们的产品最初只部署在AWS平台上,现在产品决定同时支 ...

  8. 基于 Android NDK 的学习之旅----- C调用Java

    2019独角兽企业重金招聘Python工程师标准>>> 基于 Android NDK 的学习之旅----- C调用Java 许多成熟的C引擎要移植到Android 平台上使用 , 一 ...

  9. 我的angularjs源码学习之旅2——依赖注入

    依赖注入起源于实现控制反转的典型框架Spring框架,用来削减计算机程序的耦合问题.简单来说,在定义方法的时候,方法所依赖的对象就被隐性的注入到该方法中,在方法中可以直接使用,而不需要在执行该函数的时 ...

最新文章

  1. linux 查看flash大小,Linux OpenWRT查看CPU,RAM,Flash信息参数
  2. LeetCode 2086. 从房屋收集雨水需要的最少水桶数(贪心)
  3. 联想成立数据智能事业部,前京东蓝烨回归;华为或已向多国知识产权组织提交「鸿蒙」商标申请……...
  4. python图像识别代码_用Python进行简单图像识别(验证码)
  5. linux中启动不了服务,Linux系统xinetd服务启动不了
  6. [DFS|剪枝] leetcode 22 括号生成
  7. IBGP的自己下一跳,指定源命令。
  8. poj 1328 贪心+qsort
  9. Jenkins骚操作第十三章之基于Kubernetes/K8S构建Jenkins持续集成平台(上)
  10. OpenProj打开不了或者提示”Failed to load Java VM Library”的错误的解决方案
  11. 各种距离 欧式距离、曼哈顿距离、切比雪夫距离、闵可夫斯基距离、标准欧氏距离、马氏距离、余弦距离、汉明距离、杰拉德距离、相关距离、信息熵...
  12. 雇佣兵战斗力c语言原理,暗黑2单机弓箭亚马逊应该怎么选技能和雇佣兵
  13. 远程控制计算机控制不,远程控制电脑为什么打不了字
  14. Python queue
  15. MyBatis-Plus 代码生成器最新版配置方案
  16. 聊聊RESTful - 科普篇
  17. Python:如何方便地用Python发outlook邮件
  18. ​2021年美云智数北京巡展 中粮家佳康、爱玛、红星酒业
  19. Oracle GoldenGate视频教程
  20. unity三维云图_unity3d 温度云图shader

热门文章

  1. 为什么要进行无功补偿?无功补偿的原理、形式是什么?
  2. SVG无功补偿,MATLAB仿真,三相静止无功发生器,静止同步补偿器,动态无功发生器,动态无功补偿。
  3. ifafu最新版本android,ifafu最新版下载
  4. oracle:一个update修改两张表
  5. php做热度推荐算法,基于Hacker News的内容热度推荐算法
  6. office2019 图目录_施工进度横道图不会做?18份计划模板收藏好,输入参数迅速成图...
  7. 4步建立自己的交易体系
  8. yahoo军规的思考
  9. 17、ADS使用记录之AB类功放设计续集
  10. python数据科学实践指南_Python数据科学实践指南