在做项目的过程中,出现了和对象数组进行双向绑定的需求,如果是使用vue的话,只需要一个v-model就解决了,但是原生小程序就不行啦,但是这都是无法难住大佬们的,这次找到了一个大佬的解决方式,趁机学习学习。
一、原生小程序的双向绑定
官方文档地址:简易双向绑定 | 微信开放文档 (qq.com)
1、浅读微信小程序的官方文档,可以得到
- 好消息:从基础库 2.9.3 开始,使用model可以实现简易的双向绑定
- 坏消息:并不支持data.a的形式,也就是说,基本类型可以,但是复杂类型是打咩的
2、基本使用
使用的方式非常简单,在value的前面加上model:就可以啦
<input model:value="{{value}}" />
3、在自定义组件中传递双向绑定
(1)现在你有一个组件了,这是一个输入框,使用双向绑定
<input model:value="{{myValue}}" />
// custom-component.js
Component({
properties: {
myValue: String
}
})
<custom-component model:my-value="{{pageValue}}" />
(3)如果你使用了setData
setData会改变data中的数据,对应的,输入框中的数据也将会发生改变
二、实现复杂对象的双向绑定
来自Michaelfu的实现(双层对象):微信小程序实现双向绑定 - 掘)
1、实现思路
通过输入框的输入事件,获取输入框的值,然后将这个值使用setData来进行更新。但是一个个获取值,然后使用setData更新太麻烦啦,所以将这个方法进行封装
module.exports = {
inputgetName(e) {
// 从事件对象 e 中获取输入框的 data-name 属性值
let name = e.currentTarget.dataset.name;
// 用于存储更新后的数据对象
let nameMap = {};
// 通过判断 name 是否包含点符号 . 来确定是否存在多层级的数据绑定关系
if (name.indexOf('.') !== -1) {
let nameList = name.split('.');
// 判断多层级的第一个属性是否存在于 this.data 中,
如果存在,将其保存到 nameMap 中,
否则创建一个空对象并保存到 nameMap
if (this.data[nameList[0]]) {
nameMap[nameList[0]] = this.data[nameList[0]];
} else {
nameMap[nameList[0]] = {};
}
// 将输入框的值 e.detail.value 更新到 nameMap 中
nameMap[nameList[0]][nameList[1]] = e.detail.value;
} else {
// 单层的数据直接更改
nameMap[name] = e.detail.value;
}
this.setData(nameMap);
}
};
2、使用方法
(1)为你的js找一个home,例如:我是放在了utils中,文件命名为binds.js
(2)将刚刚js代码复制进去(就是下面这个啦)
module.exports = {
inputgetName(e) {
// 从事件对象 e 中获取输入框的 data-name 属性值
let name = e.currentTarget.dataset.name;
// 用于存储更新后的数据对象
let nameMap = {};
// 通过判断 name 是否包含点符号 . 来确定是否存在多层级的数据绑定关系
if (name.indexOf('.') !== -1) {
let nameList = name.split('.');
// 判断多层级的第一个属性是否存在于 this.data 中,
如果存在,将其保存到 nameMap 中,
否则创建一个空对象并保存到 nameMap
if (this.data[nameList[0]]) {
nameMap[nameList[0]] = this.data[nameList[0]];
} else {
nameMap[nameList[0]] = {};
}
// 将输入框的值 e.detail.value 更新到 nameMap 中
nameMap[nameList[0]][nameList[1]] = e.detail.value;
} else {
// 单层的数据直接更改
nameMap[name] = e.detail.value;
}
this.setData(nameMap);
}
};
(3)在需要用到双向绑定的页面中引用
js文件:
// 引用公共app.js公共内容
var app = getApp();
// 引入双向绑定js
var binds = require('../../../utils/binds')
xml文件:
注意:控件需要bindinput
绑定inputgetName方法,data-name
属性也要与需要操作的变量匹配,否则会失效,这样就完成了。
三、扩展
上面的方式几乎可以满足大多数的场景啦,如果是多层嵌套的情况,可以将js文件的代码替换为下面的:(注明:由于时间原因还没来得及测试这段代码,谨慎使用嗷)
module.exports = {
inputgetName(e) {
// 从事件对象 e 中获取输入框的 data-name 属性值
let name = e.currentTarget.dataset.name;
// 用于存储更新后的数据对象
let nameMap = {};
// 通过判断 name 是否包含点符号 . 来确定是否存在多层级的数据绑定关系
if (name.indexOf('.') !== -1) {
let nameList = name.split('.');
// 判断多层级的第一个属性是否存在于 this.data 中,如果存在,将其保存到 nameMap 中,否则创建一个空对象并保存到 nameMap
if (this.data[nameList[0]]) {
nameMap[nameList[0]] = this.data[nameList[0]];
} else {
nameMap[nameList[0]] = {};
}
// 将输入框的值 e.detail.value 更新到 nameMap 中
nameMap[nameList[0]][nameList[1]] = e.detail.value;
} else {
// 单层的数据直接更改
nameMap[name] = e.detail.value;
}
this.setData(nameMap);
}
};
四、总结
以上是我的个人浅薄的见解,如果有问题请您指出~ 如果您读完本文有未解决的疑惑,可以在下方评论,我们一起解决。如果这篇文章对您有帮助,请点个赞给博主一点鼓励。