业务背景
在项目中有个这样的需求,一个表单里面存放的是供应商信息。同时要做成选择不同的供应商带出对应供应商的基础信息和公司资质文件,类似于一个级联操作通过选择的供应商Id,动态显示供应商信息。
思路分析
因为要实现动态显示的效果,所以我们需要请求后端接口拿到返回给前端的数据渲染到页面的表单上。这里前后端数据交互就不详细介绍了,那么下面进入实现方式。
实现方式
因为选择供应商在表单里是一个下拉框组件,第一反应使用下拉款的Chang事件。当下拉框发生改变时去请求后端,下拉框的值通常是以键值对也就是[key:"",value:""]的形式存储。这里key就是供应商的Id,value就是供应商的名称。我们只需要拿着供应商的Id去请求后端接口查出此时的供应商信息再渲染到表单上即可实现。但是问题来了,一般情况下用下拉款的Chang事件已经可以解决这个业务需求了。但是因为公司项目封装性的原因,对下拉框组件进行了统一的封装处理做成了一个组件。我们直接改组件的话会影响到其它页面的使用。所以这时我们考虑用Vue的watch也就是监听事件来处理。当监听到供应商Id这个属性发生变化时进行我们的业务处理。
Vue监听:Vue.js 提供了一个方法 watch,它用于观察Vue实例上的数据变动。对应一个对象,键是观察表达式,值是对应回调。值也可以是方法名,或者是对象,包含选项。其中分为深度监听和浅度监听,深度监听可以逐级监听到对象下的每一个属性,每个属性发生变化时都会触发回调函数。
watch 里面还有一个属性 deep
,默认值是 false
,代表是否深度监听。那么这里我们需要监听的供应商的id是在updateRequest对象里的一个属性(manuSupplier)但其实这个属性也是一个对象类型。如果用深度监听的话就会一直触发回调函数,这样对内存的消耗是很大的,对用户的体验也不好。所以我们直接监听manuSupplier.id这个属性。
immediate:true。代表在wacth里声明了updateRequest.manuSupplier.id这个方法之后立即先去执行handler方法。在这里我们不需要立即执行handler方法只需要在这个值真的发生变化也就是选择不同的供应商id时才去执行里面的回调函数,所以不设置默认就为false。下面附上代码
data() {
return {
data() {
return {
// 修改对象
updateRequest: {
hospEqpId: '',
factSerialNo: '',
deptId: '',
hospCode: undefined,
eqpProdId: '',
contrId: '',
eqpTagValueList: [],
dealerSupplierId: '',
manuSupplierId: '',
'manuSupplier': {
'id': '',
'type': '',
'supName': '',
'phone': '',
'contactName': '',
'contactPhone': '',
'license': '',
'hospCode': '',
'createDateTime': [],
'companyCertification': '',
'certificationInfoList': [],
'invalidFile': []
},
'dealerSupplier': {
'id': '',
'type': '',
'supName': '',
'phone': '',
'contactName': '',
'contactPhone': '',
'license': '',
'hospCode': '',
'createDateTime': [],
'companyCertification': '',
'certificationInfoList': [],
'invalidFile': []
}
},
// 查询供应商请求对象
querySupplierRequest: {
hospCode: '',
id: ''
}
}
}
}
}
watch: {
// 监听厂家供应商下拉框属性值改变
'updateRequest.manuSupplier': {
handler(newVal, oldVal) {
if (newVal) {
this.querySupplierRequest.hospCode = this.hospCode
this.querySupplierRequest.id = newVal
queryBySupplierIdRequest(this.querySupplierRequest).then(res => {
this.updateRequest.manuSupplier = res
})
}
}
},
// 监听经销供应商下拉框属性值改变
'updateRequest.dealerSupplier.id': {
handler(newVal, oldVal) {
if (newVal) {
this.querySupplierRequest.hospCode = this.hospCode
this.querySupplierRequest.id = newVal
queryBySupplierIdRequest(querySupplierRequest).then(res => {
this.updateRequest.dealerSupplier = res
})
}
}
}
}
写到这一步的时候我以为已经解决问题了,但是此时返回页面查看表单发现了一个问题。两次监 听执行完之后,厂家供应商的信息是错误的。厂家供应商的信息被覆盖成经销供应商的信息。
后面经过debug调式后发现在监听到经销供应商被赋值发生变化后,因为两次监听后触发回调函数请求的后端接口是同一个,并且用到的请求参数querySupplierRequest是定义在data数据源的一个常量。因为请求是ajax封装的异步请求,在你监听到经销供应商id变化后又反向给请求参数对象queryRequest的id赋值了变成了经销商的id。所以这时厂家供应商id其实被赋值成了经销商的id。这时你去请求后端其实查询的还是经销商的信息。所以在请求后端接口时不能使用同一个常量对象改成局部对象。下面附上改正后代码
watch: {
// 监听厂家供应商下拉框属性值改变
'updateRequest.manuSupplier.id': {
handler(newVal,oldVal) {
if (newVal) {
let obj={
id : '',
hospCode : ''
}
obj.hospCode=this.hospCode
obj.id=newVal
queryBySupplierIdRequest(obj).then(res => {
this.updateRequest.manuSupplier = res
})
}
}
},
// 监听经销供应商下拉框属性值改变
'updateRequest.dealerSupplier.id': {
handler(newVal,oldVal) {
if (newVal) {
let obj={
id : '',
hospCode : ''
}
obj.hospCode=this.hospCode
obj.id=newVal
queryBySupplierIdRequest(obj).then(res => {
this.updateRequest.dealerSupplier = res
})
}
}
}
}
可以看到此时厂家供应商的信息才是正确的,且每次切换供应商都会监听到去执行你的业务代码重新赋值给表单实现了联动的效果。