Blazor University (7)组件 — 双向绑定

原文链接:https://blazor-university.com/components/two-way-binding/

双向绑定

源代码[1]

注意: 如果您还没有这样做过,请在继续本节之前先执行单向绑定[2]中的步骤。

到目前为止,我们有一个包含嵌入组件的页面,并且我们组件的部分状态是从其宿主视图(Counter 页面)以名为 CurrentCounterValue 的参数的形式传递的。但是,如果我们还希望组件能够更新传递给它的状态呢?

如果您还不熟悉 EventCallback<T> 类,请阅读组件事件[3]。理想情况下,您还应该熟悉使用 Component 指令[4],并且为了更深入地了解绑定变体,您可能还希望熟悉浏览器 DOM 事件[5]

就像我们对 Counter 页面所做的那样,通过添加一个带有 onclick 事件的按钮来更新 CurrentCounterValue 的值。

<div>CurrentCounterValue in MyFirstComponent is @CurrentCounterValue
</div><button @onclick=UpdateCurrentCounterValue>Update</button>@code {[Parameter]public int CurrentCounterValue { get; set; }void UpdateCurrentCounterValue(){CurrentCounterValue++;}
}

问题在于默认情况下 [Parameter] 绑定是单向的。因此, Page.counter 的值将被赋给 MyFirstComponent.CurrentCounterValue ,因为父视图明确设置了它:

<MyFirstComponent CurrentCounterValue=@currentCount/>

但是,当 MyFirstComponent 中的属性发生更改时,组件会设置其状态的本地副本,而不是其父视图的状态。不仅如此,下次父状态更新时,它会将该值再次赋给 MyFirstComponent.CurrentCounterValue 并替换我们修改的值。

258c5ab6fd2941111c634e7a79a27ea1.gif

当状态改变时通知父组件

要解决此问题,我们需要告诉 Blazor 使用组件页面想要使用双向绑定。我们现在告诉 Blazor 绑定(即双向绑定)到该值,而不是简单地设置 CurrentCounterValue。要对参数使用双向绑定,只需在 HTML 属性前面加上文本 @bind-。这告诉 Blazor 它不仅应该将更改推送到组件,还应该观察组件的任何更改并相应地更新自己的状态。

<MyFirstComponent @bind-CurrentCounterValue=currentCount/>

注意: 将代码值(而不是常量)分配给参数时需要 @ 符号。以前我们的标记包含 CurrentCounterValue=@currentCount,但是一旦我们在参数名称前加上 @bind - currentCount 之前的 @ 符号就变得不必要了。

现在运行应用程序将在浏览器的控制台窗口中显示以下错误。

WASM:System.InvalidOperationException:“TwoWayBinding.Client.Components.MyFirstComponent”类型的对象没有与名称“CurrentCounterValueChanged”匹配的属性。

Blazor 中的双向绑定使用命名约定。如果我们想绑定一个名为 SomeProperty 的属性,那么我们需要一个名为 SomeProperyChanged 的事件回调。每当组件更新 SomeProperty 时,都必须调用此回调。

MyFirstComponent 中实现这一点

  • 添加 CurrentCounterValueChanged 事件回调。

  • UpdateCurrentCounterValue 方法从 void 更改为 async Task

  • 在增加 CurrentCounterValue 后,调用 CurrentCounterValueChanged 以通知使用者状态已更改。

<div>CurrentCounterValue in MyFirstComponent is @CurrentCounterValue
</div><button @onclick=@UpdateCurrentCounterValue>Update</button>@code {[Parameter]public int CurrentCounterValue { get; set; }[Parameter]public EventCallback<int> CurrentCounterValueChanged { get; set; }async Task UpdateCurrentCounterValue(){CurrentCounterValue++;await CurrentCounterValueChanged.InvokeAsync(CurrentCounterValue);}
}

工作原理

如果我们打开 obj\Debug\netstandard2.0\Razor\Pages 中的 Counter.razor.gs 文件,我们可以看到 BuildRenderTree 方法如下所示:

builder.OpenComponent<...MyFirstComponent>(10);builder.AddAttribute(11, "CurrentCounterValue", ...TypeCheck<System.Int32>(...BindMethods.GetValue(currentCount))
);builder.AddAttribute(12, "CurrentCounterValueChanged",...TypeCheck<...EventCallback<System.Int32>>(...EventCallback.Factory.Create<System.Int32>(this,...EventCallback.Factory.CreateInferred(this,__value => currentCount = __value,currentCount)))
);
builder.CloseComponent();

注意: 源代码已重新格式化,为简洁起见,名称空间已替换为 ...。

  • 第 5 行 执行从 Counter.currentCountMyFirstComponent.CurrentCounterValue 的单向绑定。

  • 第 15 行 每当执行 MyFirstComponent.CurrentCounterValueChanged 时,就会更新 Counter.currentCount

绑定指令

源代码[6]

我们之前介绍了指令[7]——如果您不熟悉指令,请在继续之前阅读有关它们的部分。

我们之前介绍了指令和指令属性。在本节中,我们将通过演示如何使用双向绑定来为指令属性分配值。

快速回顾一下,指令是元素中以 @ 符号开头的标识符。例如

<h1 @ref=OurReferenceToThisElement>Hello</h1>

指令属性是以 @directive:attribute 形式提供给指令的附加信息。例如,应用于 @onclick 指令的 preventDefault 属性会阻止提交按钮实际提交表单。

<input type="submit" @onclick:preventdefault>

除此之外,还可以通过以下形式为某些指令属性赋值:

<h1 @directive:attribute="someValue">Hello</h1>

尽管没有理由特别将这些属性值限制为双向绑定,但碰巧的是,目前 Blazor 框架中唯一使用此功能的地方恰好是双向绑定,这就是本节放在双向绑定部分下方的原因。

入门

注意: 尽管为了简单起见,我们将在此处使用 HTML <input> 元素,但为了获得更丰富的用户体验(添加验证等),我建议在 <EditForm> 组件中使用 Blazor <Input*> 组件(InputDate 等)。这些在表单[8]一节中介绍。

首先,我们需要一个在 @code 部分中定义了以下成员的页面,因此我们需要绑定一些内容:

@code
{private string Name;private DateTime? DateOfBirth;private decimal? BankBalance;
}

标准双向绑定

首先,我们将从标准双向绑定到 Blazor 页面的 Name 成员开始。

<label>Name = @Name</label>
<input @bind-value=Name/>

前面标记的重要部分是 @bind-value=Name。这将为 <input> 元素上名为 value 的 HTML 属性设置双向绑定,并将其绑定到 Name 成员。

如果我们现在运行我们的应用程序,我们将看到上方的 Name = @Name 文本不会更改以反映我们在 <input> 中键入的内容,直到输入元素失去焦点或我们按下 Enter 键。

使用指令属性立即检测变化

@bind 指令有一个名为 event 的指令属性。设置此指令形式的值采用以下格式:

<input @bind-value:event="x"/>

“x”的有效值是 onchangeoninput

当没有指定 :event 的值时,onchange 是假定的默认值。这是我们在运行示例时看到的行为——绑定仅在控件失去焦点或用户按下回车键时发生。

oninput:event 的唯一其他可能值,它指示 Blazor  hook 到 HTML 元素的 JavaScript oninput 事件,并在每次触发事件时更新绑定成员。这会导致每次用户更改输入中的值时立即更新绑定成员。

注意: -value 是要绑定到的 HTML 属性或 Blazor 组件属性的名称。对于 HTML 元素,前导字母为小写,对于组件属性,前导字母为大写,指令名称和绑定目标名称由 - 符号分隔。

将以下标记添加到我们的页面并运行应用程序。

<label>Name = @Name</label>
<input @bind-value=Name @bind-value:event="oninput"/>

@bind-value:event="oninput" 是指示 Blazor 使用即时更改检测的关键。首先我们告诉 Blazor 我们要将输入框的 value HTML 属性绑定到我们的 Name 成员 (@bind-value=Name),然后我们告诉 Blazor hook 到 HTML 元素的 oninput 事件,这样每次绑定都会立即发生元素的值发生变化(@bind-value:event="oninput")。

*** 指定自定义绑定格式 通过为 @bind 指令的格式属性指定一个值来指定在用户界面中使用的自定义格式。

将以下标记添加到我们的页面并运行应用程序。

<label>Date of birth = @DateOfBirth?.ToString("MMMM d, yyyy")</label>
<input @bind-value=DateOfBirth @bind-value:format="yyyy-MM-dd"/>

当应用程序运行时,输入 ISO 格式的日期(例如 1969-07-21)。虽然日期在 <label> 中显示为 July 21, 1969,但 <input> 控件以我们在 @bind-value:format="yyyy-MM-dd" 中指定的 ISO 显示它。

注意: 输入的任何与指定格式不匹配的值都将被丢弃。因此,我们不能设置 @bind-value:event="oninput",因为 Blazor 会尝试在每次按键时解析输入,但输入的值不可能在单次按键后有效,因此输入值将干脆消失。这是我建议在编辑数据时在 EditForm[9] 中使用 Blazor <Input*> 组件的原因之一,因为这使我们能够使用诸如 <InputDate> 之类的组件。

如 Descending from InputBase[10] 部分所述,Blazor 输入组件具有一对互补的受保护方法,用于将绑定值转换为字符串和从字符串转换为字符串。

工作原理

@bind 指令不会添加代码来直接绑定到我们的成员,而是简单地将其转换为字符串值/从字符串值转换。相反,它通过 BindConverter 重定向当前值的表示和输入值的解析。

如果我们查看 Blazor 为单向绑定(例如 class=@OurCssClass)生成的 .cs 文件,我们会看到 C# 看起来像这样(为简洁起见进行了编辑)。

protected override void BuildRenderTree(RenderTreeBuilder __builder)
{_builder.AddAttribute(1, "class", OurCssClass);
}

现在,如果我们查看生成的双向绑定文件,我们将看到类似于以下(有删减)代码的内容,用于显示该值:

protected override void BuildRenderTree(RenderTreeBuilder __builder)
{_builder.AddAttribute(1, "value",...BindConverter.FormatValue(Name));

以及类似于以下(有删减)代码,用于将用户输入转换回绑定成员。

__builder.AddAttribute(11, "onchange",...EventCallback.Factory.CreateBinder(this, __value => Name = __value, Name));
}

代码 hook 到 HTML onchange 事件,然后在事件触发时设置我们的成员值。

设置 @bind-value:format 指令属性值时的不同之处在于我们提供的格式在生成的代码中传递给了 BindConverter.FormatEventCallback.Factory.CreateBinder

...BindConverter.FormatValue(Name, format: "yyyy-MM-dd");
// and
CreateBinder(...., format: "yyyy-MM-dd");

指定自定义 culture

世界上的人们有不同的习俗和文化,这是使世界变得如此有趣的原因之一。不幸的是,这也是使编写软件更加困难的原因之一。

将以下标记添加到我们的页面:

<label>Bank balance = @BankBalance</label>
<input @bind-value=BankBalance @bind-value:culture=Turkish/>

并确保将以下成员添加到 @code 部分的页面中:

private CultureInfo Turkish = CultureInfo.GetCultureInfo("tr-TR");

输入值 12.42 可能会期望余额超过 12 土耳其里拉,但正如我们所见,我们只是不小心给了某人 1,242 土耳其里拉。当然,居住在土耳其的人会知道要输入 12,42,但这凸显了当我们的应用程序打算在其他国家/地区使用时正确指定文化的必要性。

fbb0915993f497d4eeafc91f936d5c30.png

format 指令属性一样,指定的 @bind-value:culture 将作为命名(可选)值传递给 BinderBindConverter

如果您还没有听说过土耳其测试,那么我建议您阅读这篇优秀的文章[11]

参考资料

[1]

源代码: https://github.com/mrpmorris/blazor-university/tree/master/src/Components/TwoWayBinding

[6]

源代码: https://github.com/mrpmorris/blazor-university/tree/master/src/Components/BindingDirectives

[11]

这篇优秀的文章: http://www.moserware.com/2008/02/does-your-code-pass-turkey-test.html

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

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

相关文章

【iCore3 双核心板】例程二十二:LAN_UDP实验——以太网数据传输

实验指导书及代码包下载&#xff1a; http://pan.baidu.com/s/1kTPlJMJ iCore3 购买链接&#xff1a; https://item.taobao.com/item.htm?id524229438677 转载于:https://www.cnblogs.com/xiaomagee/p/5084291.html

tomcat出现5个using_当猫咪出现这5个迹象,主人就要给猫咪换猫粮了

养宠的朋友们你们知道吗&#xff1f;猫咪最好是要定期换粮&#xff0c;这样才有助于猫咪营养平衡&#xff0c;并且避免猫咪对单一食物感到厌烦哦&#xff01;但是应该多久给猫咪换一次粮呢&#xff1f;如果你家猫咪出现这5个迹象时&#xff0c;那就是提醒你需要更换猫粮了&…

C# Barrier类

对于同步&#xff0c;Barrier 类非常适用于其中工作有多个任务分支且以后又需要合并工作的情况。Barrier 类用于需要同步的参与者。激活一个任务时&#xff0c;就可以动态地添加其他参与者&#xff0c;例如&#xff0c;从父任务中创建子任务。参与者在继续之前&#xff0c;可以…

Android版哆啦A梦连连看游戏源码完整版

本代码主本人编写&#xff0c;没版权问题&#xff0c;另有其他少量小游戏和应用源码稍后会陆续上线!哆啦A梦连连看包括了2种游戏模式和60关卡&#xff0c;并可以通过修改配置文件很方便的实现自行增加新关卡。采用andengine游戏引擎开发&#xff0c;内置了趣米广告和用户统计fl…

javascript 高级程序设计_重读《JavaScript高级程序设计》

最近自己在休假&#xff0c;打算闭门几天将《JavaScript高级程序设计》(第3版)这本良心教材再回顾一遍。目前自己进入前端领域两年多&#xff0c;现在重读并记录下这本教材的“硬”知识点 。本文原文链接请戳 重读《JavaScript高级程序设计》​link.jianshu.com函数没有重载ECM…

[异常解决] ubuntu上安采用sudo启动的firefox,ibus输入法失效问题解决

采用sudo启动的应用是root权限的应用&#xff0c; ibus失效是因为ibus的初始配置采用user权限: 而root下运行的firefox输入法的配置还是停留在默认情况~ 解决方案是在shell下以root权限运行ibus&#xff0c;然后进行配置&#xff1a; 1、shell下输入&#xff1a;sudo ibus-setu…

Avalonia-.NET 的跨平台 UI 框架

简介Avalonia 是 dotnet 的跨平台 UI 框架&#xff0c;提供灵活的样式系统并支持 Windows、Linux、macOS 等多种操作系统。Avalonia 已经成熟并且可以投入生产。我们还在测试版中支持 iOS、Android&#xff0c;并在早期阶段通过 WASM 支持浏览器。使用在WPF中&#xff0c;当你在…

Skype for TV停止支持 三星确认今年6月移除该应用

微软热门视频聊天和语音通话服务Skype自今天开始将不再支持智能电视。用户无法在智能电视上登录或者重置密码。在停止支持之后&#xff0c;由电视制造厂商自行决定移除Skype应用或者继续提供非支持版本。三星已经确认将于2016年6月2日开始在旗下的智能电视中移除Skype服务&…

CactiEZ V10.1 中文版 Cacti中文解决方案+使用教程(2)

配置被监控的Windows主机说明&#xff1a;要使用CactiEZ监控一台Windows主机&#xff0c;需要在被监控的主机上面安装snmp&#xff08;简单网络管理协议&#xff09;1、下面开始安装配置snmp开始-设置-控制面板-添加或删除程序-添加删除Windows组件找到管理和监视工具&#xff…

AgileConfig 1.6.0 发布 - 支持服务注册与发现

大家好&#xff0c;好久没有输出博文了&#xff0c;一是因为比较忙&#xff0c;另外一个原因是最近主要的精力是在给 AgileConfig 添加一个新的功能&#xff1a;服务注册与发现。先说说为什么会添加这个功能。我自己的项目是用 Consul 来做为服务注册发现组件的。自从我上线了 …

快速排序算法_Python实现快速排序算法

排序是算法的入门知识&#xff0c;应用广泛&#xff0c;且在程序员面试中&#xff0c;经常被提及&#xff0c;其中最常考的两大排序算法为快速排序与归并排序&#xff0c;本篇将使用Python语言来分析了解快速排序算法。思想 快速排序是一种非常高效的排序算法&#xff0c;采用 …

用户反馈KB3189866累积更新出现卡在95%进度情况

伴随着本月的补丁星期二活动&#xff0c;微软面向Windows 10推出了三款累积更新。和以往相同&#xff0c;部分用户反馈称无法安装本次更新并导致出现各种问题。很多读者反馈称KB3189866累积更新会在45%或95%的时候卡住不动&#xff0c;无法执行和完成安装。 在尝试过几种临时解…

UVa1607 poj1435 UVaLive1686 Gates

填坑系列(p.246) 由函数连续性得满足二分性 1 #include<cstdio>2 #include<cstring>3 #include<cstdlib>4 #include<algorithm>5 #include<iostream>6 7 using namespace std;8 9 void setIO(const string& s) { 10 freopen((s ".…

c语言入门数组,C语言入门之数组(2)

二维数组前面介绍的数组只有一个下标&#xff0c;称为一维数组&#xff0c; 其数组元素也称为单下标变量。在实际问题中有很多量是二维的或多维的&#xff0c; 因此C语言允许构造多维数组。多维数组元素有多个下标&#xff0c; 以标识它在数组中的位置&#xff0c;所以也称为多…

IOS开发--TextField

2019独角兽企业重金招聘Python工程师标准>>> //初始化text field的位置和大小UITextField *text [[UITextField alloc] initWithFrame:CGRectMake(20, 20, 130, 130)];//设置边框样式//样式有很多种&#xff0c;点进去查看text.borderStyle UITextBorderStyleRoun…

IOS之学习笔记十五(协议和委托的使用)

1、协议和委托的使用 1)、协议可以看下我的这篇博客 IOS之学习笔记十四(协议的定义和实现) https://blog.csdn.net/u011068702/article/details/80963731 2)、委托可以叫代理&#xff0c;实现协议的类的对象可以叫委托对象或者代理对象 3)、关键就是我们在控制器里类(获取数…

Dapr 源码解析 系列文章汇总

Dapr背后的架构模式是符合未来架构趋势&#xff08;多运行时架构&#xff09;和云原生发展趋势的。知乎 iyacontrol 对 Dapr的意义有一个很好的概括&#xff1a;对于小公司&#xff0c;甚至没有基础架构和中间件团队的公司&#xff0c;Dapr 提供了开箱即用的基础设施功能&#…

cstring判断包含字符串_Python字符串方法之-解决判断问题

19、endswith()描述&#xff1a;判断字符串是否以指定字符或子字符串结尾。语法&#xff1a;str.endswith("suffix", start, end) 或str[start,end].endswith("suffix") 用于判断字符串中某段字符串是否以指定字符或子字符串结尾。—> bool 返回值为布尔…

Angular变化检测机制

2019独角兽企业重金招聘Python工程师标准>>> 在使用Angular进行开发中&#xff0c;我们常用到Angular中的绑定——模型到视图的输入绑定、视图到模型的输出绑定以及视图与模型的双向绑定。而这些绑定的值之所以能在视图与模型之间保持同步&#xff0c;正是得益于Ang…