输入与双向绑定¶
双向绑定既将数据上下文中的属性显示到输入控件,也将用户输入写回对应属性。应用修改属性并发出通知时,控件随之更新;用户编辑控件时,按配置的提交时机更新数据上下文。
本章示例使用可观察对象的可写属性。对象定义和通知写法见数据上下文,类型范围见数据类型。
输入值与绑定目标¶
| HTML 元素 | 绑定目标 | 数据上下文中的值 | 默认模式 |
|---|---|---|---|
input、textarea |
@value |
字符串或可转换的数值 | twoWay |
input type="checkbox" |
@checked |
布尔值 | twoWay |
单选 select |
@value |
与选项值对应的标量 | twoWay |
多选 select |
@values,或 multiple 配合 @value |
标量 Array | twoWay |
option |
@value |
当前选项的值 | oneWay |
@value 读取和设置控件当前的输入值,@checked 对应实际勾选状态。它们不仅是设置 HTML 源文件中的初始属性。
双向绑定需要可写属性
路径必须能定位可写的业务属性或可写集合项,控件也必须提供相应输入行为。Price * Quantity、Name ?? 'Guest' 等计算结果不能直接写回;设置 twoWay 不会自动反推业务属性。
文本输入¶
单行与多行文本¶
将字符串属性 Name 绑定到单行输入框,将 Description 绑定到文本域:
<label>
Name
<input type="text" @value="{Name}">
</label>
<label>
Description
<textarea @value="{Description}"></textarea>
</label>
输入控件的 value 默认使用 twoWay,不必重复声明模式。两个控件分别读写当前数据上下文中的 Name 和 Description。
同一个属性可以同时用于编辑和显示:
<input @value="{Name}">
<p>Player: {{ Name }}</p>
用户编辑输入框后,设置器更新 Name 并发出属性通知,下面的文本随之更新。将数据写入属性和通知其他绑定是两个环节;属性设置器应保留正确的通知逻辑。
设计时占位值¶
可以为设计时预览提供静态文字,运行时使用绑定值:
<input value="Player name" @value="{Name}">
<textarea @value="{Description}">Character description</textarea>
静态内容不作为业务数据初始化方案。需要初始名称或描述时,应在可观察对象中设置初值。
数值输入¶
输入框可绑定整数或浮点属性,例如整数属性 Health:
<input type="number" @value="{Health}">
显示时,数值转换为文本;写回时,输入文本按 Health 的实际类型转换,并检查其范围。HTML 的 type="number" 不改变业务属性类型,也不能代替写回校验。
无效输入不会成为有效数值
空文本、不能完整解析的数字、越界值或不符合目标类型的内容会使写回失败,原属性值保留。例如,整数属性不能接收 12.5,无符号属性不能接收负数。绑定不会将失败输入自动替换为 0,也不自动生成校验提示。
需要单位、特殊数字格式或其他业务转换时,应由应用提供适用的转换器及反向转换。不要将带单位的显示文本直接写入数值属性。
勾选状态¶
复选框使用 @checked 绑定布尔属性:
<label>
<input type="checkbox" @checked="{IsReady}">
Ready
</label>
IsReady 为 true 时勾选,为 false 时取消勾选。用户切换勾选状态后,布尔值写回 IsReady。
不要用 @value 表示是否勾选,也不要将字符串 'true'、'false' 当作布尔属性。
单项选择¶
单选 select 通过 @value 绑定选中的选项值:
<select @value="{Difficulty}">
<option value="easy">Easy</option>
<option value="normal">Normal</option>
<option value="hard">Hard</option>
</select>
Difficulty 为 normal 时选中 Normal。用户选择 Hard 后,写回字符串 hard,不是显示文字 Hard,也不是选项下标。
选项值也可以转换为业务属性的数值类型:
<select @value="{QualityLevel}">
<option value="1">Low</option>
<option value="2">Medium</option>
<option value="3">High</option>
</select>
若 QualityLevel 是整数属性,选择 High 时写回数值 3。
从集合生成选项¶
select 支持通过 @for 绑定选项集合,单选和多选均可使用。@for 决定可选项,@value 或 @values 决定当前选中的值,两者分别绑定。
假设 Difficulties 的对象项包含 Id 和 Name,可通过集合绑定生成选项:
<select @value="{Difficulty}" @for="{Difficulties}">
<option @value="{Id}">{{ Name }}</option>
</select>
select 的 Difficulty 从外层数据上下文读取;每个 option 的 Id 和 Name 从当前集合项读取。option 的 @value 提供选项值,不负责将选中结果写回集合项。
绑定的属性值优先于静态 selected。属性为空或没有对应选项时,不应假定静态选中项会自动恢复;应用应提供与选项匹配的有效初值。
多项选择¶
使用 @values 将多选结果绑定到标量 Array:
<select @values="{SelectedTags}">
<option value="fire">Fire</option>
<option value="ice">Ice</option>
<option value="wind">Wind</option>
</select>
SelectedTags 是字符串数组或列表属性。它包含 fire 和 wind 时,对应两项选中;用户改变选择后,写回当前全部选中值。
也可以显式声明 multiple 并使用 @value:
<select multiple @value="{SelectedTags}">
<option value="fire">Fire</option>
<option value="ice">Ice</option>
<option value="wind">Wind</option>
</select>
两种写法含义相同。@values 本身启用多选,无需再填写 multiple。
多选同样可以通过 @for 生成选项。假设 Tags 的对象项包含 Id 和 Name:
<select @values="{SelectedTags}" @for="{Tags}">
<option @value="{Id}">{{ Name }}</option>
</select>
Tags 提供可选项,SelectedTags 保存选中的 Id。循环内的 Id 和 Name 属于当前标签对象,SelectedTags 则从外层数据上下文读取。集合增删及选项属性变化需要对应通知,具体规则见集合绑定。
多选写回完整数组
多选提交的是按选项显示顺序排列的新数组,不是用户点击的顺序,也不是逐项增删原集合。因此,接收选择结果的属性必须允许整体赋值。不能用单个字符串、Map、Set 或对象数组接收多选结果;不要在同一元素上同时声明 @value 和 @values。
数值数组按元素类型转换全部选中值。任一选项转换失败时,不会将已成功转换的部分结果写入原属性。
绑定模式¶
输入目标默认使用 twoWay,可以按需要改为其他模式:
| 模式 | 输入目标的行为 |
|---|---|
twoWay |
读取数据上下文显示,并将输入写回 |
oneWay |
从数据上下文更新控件,不将用户修改写回 |
oneTime |
初始化时读取,不持续跟随属性变化,也不写回 |
oneWayToSource |
将控件值写入数据上下文,不从属性更新控件 |
例如,只让数据上下文更新输入框:
<input @value="{Name, mode=oneWay}">
oneWay 只限制绑定方向,不禁止控件接受键盘或鼠标输入。需要只读或禁用交互时,应使用控件实际支持的相应设置。
提交时机¶
mode 决定更新方向,update 决定用户输入何时写回。提交时机仅对 twoWay 和 oneWayToSource 的写回方向有意义,不延迟数据上下文对控件的正常更新。
可以通过 update 显式指定提交时机:
update=input:在编辑过程中值发生变化、控件产生input事件时提交。例如,文本从A变成AB会产生一次输入变化;删除、粘贴等操作也可以产生该事件。它表示值发生了编辑变化,不表示用户已经结束编辑。update=change:用户提交了一次实际变化、控件产生change事件时提交。例如,切换选项、勾选复选框会提交新状态;文本框通常在编辑结束并失焦后,且值相较上次提交确实发生变化时产生该事件。它强调确认后的值变化,不等同于所有失焦操作。update=blur:控件失去焦点时提交已有的待写回值。它以焦点变化为提交时机,不要求控件先产生change事件;仅失焦而没有待写回变化时,不会凭空产生新数据。update=explicit:不因input、change或blur自动写回。编辑值保持待提交状态,直到应用通过实际提供的写回入口显式提交。
input 是输入事件,update 是绑定选项
input 描述控件中的值如何变化;update 决定绑定何时把该值写回数据上下文。update=input 的含义是选择 input 事件作为提交时机,并不是额外触发一个名为 update 的事件。
input 不等于“按了一次键”。文本从 A 变成 AB、删除或粘贴都可能产生 input;中文输入法组字期间也可能产生它,因此不能将每次 input 都理解为完成了一次文字输入。平台如何处理输入法组字及何时允许写回,以其输入接入规则为准。
即使文本框产生了 input,默认的 update=blur 也不会因此立即写回,而是等待失焦;显式设置 update=input 才选择随输入变化提交。
省略 update 时,各输入元素的默认提交时机如下:
| HTML 元素 | 目标属性 | 默认提交时机 |
|---|---|---|
<input type="text">(含省略 type) |
@value |
blur |
<input type="search"> |
@value |
blur |
<input type="email"> |
@value |
blur |
<input type="url"> |
@value |
blur |
<input type="tel"> |
@value |
blur |
<input type="password"> |
@value |
blur |
<input type="number"> |
@value |
blur |
<textarea> |
@value |
blur |
<input type="checkbox"> |
@checked |
change |
<input type="radio"> |
@checked |
change |
<input type="range"> |
@value |
change |
<input type="color"> |
@value |
change |
<input type="date"> |
@value |
change |
<input type="datetime-local"> |
@value |
change |
<input type="month"> |
@value |
change |
<input type="week"> |
@value |
change |
<input type="time"> |
@value |
change |
<input type="file"> |
@value |
change |
<select> |
@value |
change |
<select multiple> |
@value |
change |
<select> |
@values |
change |
以上按元素类型列出 SDK 的默认提交规则;颜色、日期、文件选择等专用控件是否可用,以各平台插件的实际支持范围为准。@value 不表示提供浏览器的文件对象或上传接口。
单向显示绑定不写回数据上下文,update 不改变绑定方向。
默认提交¶
<input @value="{Name}">
文本输入框默认在失去焦点时写回 Name。如果需要随输入更新,应显式设置 input:
<input @value="{Name, update=input}">
失去焦点时提交¶
<input @value="{Name, update=blur}">
编辑期间控件可显示尚未提交的内容,离开控件后再写回 Name。也可以使用展开写法:
<input @value="{Name}" @value.update="blur">
多选目标使用自己的选项名称,例如 @values.update="blur"。
显式提交¶
<input @value="{Name, update=explicit}">
此时输入不会自动写回。仅添加按钮、触发 HTML submit 或绑定 @onclick 命令,都不等于已经实现显式提交;应用需要提供实际的绑定写回入口。是否提供相应操作及其调用方式,以各平台插件的公开能力为准。
不要依赖命令隐式提交输入
输入写回与事件命令是不同操作。命令不保证在执行前自动提交所有控件,尤其不能替代 update=explicit 的写回调用。若业务操作必须读取最新值,应明确配置提交时机及处理顺序。
嵌套属性与集合项¶
双向绑定也可以定位嵌套对象和集合项的可写属性:
<input @value="{Weapon.Name}">
<input @value="{Items[SelectedIndex].Name}">
路径中间的对象、数组位置或 Map 键必须有效。动态下标或上下文变化后,后续输入针对当前解析出的属性,不继续写入旧对象。
循环中可编辑对象项的属性:
<ul @for="{Items}">
<li><input @value="{Name}"></li>
</ul>
标量 Array 项可使用 @this 或 @item-as 别名定位当前项;集合本身须支持对应位置写入:
<ul @for="{Numbers}">
<li><input @value="{@this}"></li>
</ul>
@index、@key 和 @items 是循环信息,不能作为可编辑字段写回;Set 也不提供下标赋值能力。
平台输入支持范围¶
绑定语法由 SDK 提供,焦点、键盘、鼠标、输入法和控件输入事件由平台接入。本文中的声明不代表所有 HTML 输入类型或浏览器控件行为均已支持;具体输入类型、输入法及焦点限制请阅读各平台文档。
界面可以显示初值但编辑后没有更新时,依次检查:
- 当前数据上下文与属性路径是否正确。
- 属性是否可写,输入值能否转换为目标类型。
- 模式是否允许写回,提交时机是否已经满足。
- 属性设置器是否发出通知,使其他绑定更新。
- 平台是否向页面传递了焦点和输入事件,以及日志中是否有转换或绑定错误。