Blazor学习之旅(5)数据绑定

5bd88dfd863f244841f7bdbb22e93b4b.jpeg

【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"
}

实现效果:

556bf5e11b7b551905756b10b0e78445.gif

设置绑定值的格式

在很多场景中,我们可能需要对日期进行本地化的格式转换。这里,我们就可以借助@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()方法也说明它是线程安全的。

实现效果:

873ce2b26bfdbf78d4acaf4f0918bc33.gif

在一个更真实常见的场景中,我们可能希望实现数据实施修改的联动更新,类似于下面的例子。

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";
}

最终效果:

3f271f8325603f5eb97986ef4e18108b.gif

组件参数绑定的最佳实践

我们可以在多层嵌套的组建中绑定组件参数,但是我们必须遵循这类单向数据绑定的流程:

  • 更改通知是逐级向上流动

  • 新的参数值是逐级向下流动

一个推荐的方式是只在父组件中存储源数据,以此避免在状态需要更新时容易产生的混淆。

例如,下面这个例子:

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来向上通知。

最终效果:

8bb49d3f011e00712db090e8546623bd.gif

小结

本篇,我们了解了数据如何在Blazor中进行数据的绑定。

下一篇,我们学习一下在Blazor中数据绑定的各种花样。

参考资料

Microsoft Docs,《与Blazor Web应用中的数据交互》

Microsoft Docs,《Blazor数据绑定》

236dc049ae7e4c423724172596c339c4.gif

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

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

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

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

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

fb0b67a1e789c06e786a00ba6b442c19.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/281894.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chrome开启touch屏幕点击事件

2019独角兽企业重金招聘Python工程师标准>>> 在chrome浏览器输入 chrome://flags/#top-chrome-md 找到属性 UI Layout for the browsers top chrome 选择Hybrid&#xff0c;重启chrome&#xff0c;如下图&#xff1a; 转载于:https://my.oschina.net/swingcoder/blo…

Linux服务器重启后crs_stat -t 命令无法正常使用以及解决思路

前提&#xff1a;在Linux系统中安装ASM&#xff0c;安装完ASM和Oracle数据库时都是正常使用的&#xff0c;但在重启服务器后Oracle相关命令不识别。1、[gridudevasm:/home/grid]$crsctl status res -t -bash: crsctl: command not found2、查看环境变量是否正常&#xff0c;命令…

python 打开某个exe_python定时检查启动某个exe程序(如果exe挂了)

详见代码如下&#xff1a;import threadingimport timeimport osimport subprocessdef get_process_count(imagename):p os.popen(tasklist /FI "IMAGENAME eq %s" % imagename)return p.read().count(imagename)def timer_start():t threading.Timer(120,watch_fu…

Google发布了Tensorflow Lite,用于移动电话的神经网络库

Google的工程副总Dave Burke宣布了一个专门针对移动电话而优化的Tensorflow新版本。\\这一新的软件库称为Tensorflow Lite&#xff0c;允许开发人员在用户的移动电话上实时地运行人工智能应用。据Burke介绍&#xff0c;该库在设计上力求更快和更小的同时&#xff0c;依然支持最…

4. ZooKeeper 基本操作

ZooKeeper的数据模型及其API支持以下九个基本操作&#xff1a; 操作描述create在ZooKeeper命名空间的指定路径中创建一个znodedelete从ZooKeeper命名空间的指定路径中删除一个znodeexists检查路径中是否存在znodegetChildren获取znode的子节点列表getData获取与znode相关的数据…

微软正式发布Azure Functions 2.0

微软正式发布Azure Functions的第二个版本&#xff0c;这是一个事件驱动的、按需计算的Azure平台服务。与版本1相比&#xff0c;新版本的Azure Functions包含的多项特性使开发人员可以更轻松地构建可伸缩的无服务器应用程序。2016年初&#xff0c;微软首次推出Azure Functions预…

CSS-下拉导航条

Web网站中很多时候都会出现下拉导航条&#xff0c;有的是通过CSS实现&#xff0c;有的通过JavaScript插件实现&#xff0c;其实CSS实现起来比较简单,先来看一个简版的下拉菜单: Html代码通过ul列表实现: 123456789101112131415161718<ul class"nav"> <li>…

#UnityTips# 2017.11.14

hi&#xff0c;all。最近比较忙&#xff0c;所以更新也比较慢了。 今天就来和大家分享一个小Tip&#xff0c;它是关于UGUI的坑的。 使用过UGUI的朋友们都知道&#xff0c;Canvas的渲染方式有三种&#xff1a; Screen Space OverlayScreen Space CameraWorld Space其中后两者都需…

java 复制excel_Java 复制Excel工作表

本文归纳了关于Java如何复制Excel工作表的方法&#xff0c;按不同复制需求&#xff0c;可分为&#xff1a;1. 复制工作表1.1 在同一个工作簿内复制工作表1.2 在不同工作簿间复制工作表2. 复制指定单元格数据对于复制方法copy()&#xff0c;这里简单整理了一个表格&#xff0c;其…

Blazor学习之旅(4)数据共享

【Blazor】| 总结/Edison Zhou大家好&#xff0c;我是Edison。前几天没有发布本篇就发布了第五篇&#xff0c;属于操作失误哈&#xff0c;这次把第四篇补上&#xff01;本篇&#xff0c;我们来了解下在Blazor中数据是如何共享的&#xff0c;组件之间又该如何传递参数。关于Blaz…

# 20172307 2018-2019-1 《程序设计与数据结构》第5周学习总结

20172307 2018-2019-1 《程序设计与数据结构》第5周学习总结 教材学习内容总结 查找 1.线性查找&#xff1a;从该列表头开始依次比较每一个值&#xff0c;直至找到该目标元素。2.二分查找法&#xff1a;二分查找是从排序列表的中间开始查找&#xff0c;如果没有在那个中间元素则…

IBM 的大型机 z Systems 引入 Go 语言

据 cbronline 报道&#xff0c;IBM 正把 Go 语言运用到旗下的大型机上。 IBM 将开源的 Go 语言引入到 z Systems 大型机后&#xff0c;可以给用户多一个的选择&#xff0c;即在大型机上使用 Linux 或基于 Go 的应用&#xff0c;同时也使大型机更加灵活。Go 语言在高并发的网络应…

WPF-10 逻辑树和可视化树

我们在WPF-03 资源之Resources结尾中介绍逻辑树和可视化树的基本概念&#xff0c;我们这节来介绍这两棵树逻辑树&#xff08;Logical Tree&#xff09;逻辑树是由每个控件的节点组成&#xff0c;本质上就是XAML文件中的UI元素&#xff0c;我们可以通过LogicalTreeHelper类提供的…

国产车崛起粉碎德日工业神话

由于二战战败&#xff0c;德国一大批顶尖人才被美苏瓜分&#xff0c;战败国地位和人才断层导致德国工业基本是第二次工业革命的产物&#xff0c;专精于机械、化工等传统行业&#xff0c;并有巴斯夫、拜尔、大众、戴姆勒、宝马等一批世界级企业。不过&#xff0c;德国世界级的IT…

java hibernate 分页查询_4 Hibernate HQL查询,分页查询

/*** HQL查询的一个例子*/public static void hql(){Session s null;try{s HibernateUtil.getSeesion();//final String hql "from User as u where u.name?";final String hql "from User as u where u.name:name";final Query query s.createQuery…

Linux -sed

sed &#xff0c;查找sed -n /root/p passwd #列出passwd中有root的行 sed -nr /ot/p passwd #sed -r grep -E 都是进行脱意 sed -nr /0{2}/p passwd #匹配两次o的 sed -nr /root|bus/p passwd #匹配root 或者bus的 sed -n 2p passwd # 查找指定的行sed -n 2,5p passwd # 查找…

node安装问题

1.最好安装到默认路径&#xff0c;手贱安到了D盘&#xff0c;升级npm各种出错。 明明升级成功&#xff0c;查看版本时&#xff0c;确显示依然是老的版本。 原因&#xff1a;升级的是C盘的node_modules中的npm&#xff0c;执行时确是D盘node自带的npm&#xff0c;不知道为啥。。…

通过url来设置log4j的记录级别

2019独角兽企业重金招聘Python工程师标准>>> 直接看代码。 import org.apache.log4j.AppenderSkeleton; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotati…

通过用户模型,对数据库进行增删改查操作

增加&#xff1a;user db.session.add(user)db.session.commit() #增加 user User(username JACKSON,password0328 ) db.session.add(user) db.session.commit() 查询&#xff1a;User.query.filter(User.username mis1114).first() #查询 userUser.query.filter(User.usern…

Android OpenGL ES(七)----理解纹理与纹理过滤

1.理解纹理 OpenGL中的纹理能够用来表示图像。照片&#xff0c;甚至由一个数学算法生成的分形数据。每一个二维的纹理都由很多小的纹理元素组成。它们是小块的数据&#xff0c;类似于我们前面讨论过的片段和像素。要使用纹理&#xff0c;最经常使用的方式是直接从一个图像文件载…