1、问题描述
准确说,是在dialog的el-from组件中的el-select组件,打开模态框时,对select绑定的数据重新赋值value1,select视图层状态会变成value1,但之后变更option选项,select视图层*不再更新。
一般是在新建或编辑数据,弹出dialog模态框,在弹窗的表单里对该条数据进行编辑修改。
<el-dialog :visible.sync="dialogEdit">
<el-form ref="editValidateForm" :model="editValidateForm">
...
<el-form-item prop="workType" label="类型:">
<el-select v-model="editValidateForm.workType"
placeholder="请选择类型"
@change="handleChange">
<el-option v-for="(item,index) in types"
:key="index"
:label="item.name"
:value="item.value">
{{item.name}}
</el-option>
</el-select>
</el-form-item>
</el-form>
</el-dialog>
editValidateForm在data里会有一组初始数据:
export default {
name: "test",
data(){
return{
editValidateForm:{
...
workType:'',
}
}
},
methods:{
open(){},
handleChange(){}
}
}
2、问题情况1
如果是单纯地dialogEdit = true打开模态框,不对表单数据进行重新赋值,是不会出现问题的。
如果有问题,可能是vue或者element的版本bug,我用的elemnt2.4.6,没有这个bug,这里也不针对这种情况说明。
3、问题情况2
如果在打开dialog的同时,对这个数据进行赋值:
this.editValidateForm.worktType = 1
会看到select的选中项变为1,但是之后再选其他option无法选中。
其实这里只是视图层没有更新,在select的change方法
里监听,this.editValidateForm.worktType
的值是改变了的。
出现这个问题好像是因为下拉框数据是循环掉别的接口得来的,因为数据层次太多,render函数没有自动更新,需手动强制刷新
3.1 尝试1
由此,我想到vue文档里说过,利用索引直接设置数组一个原本不存在的项时
this.items[index] = newValue
不会使视图更新,为了针对这种情况,vue提供了set方法vm.$set(target,propertyName/index,value)
,以此赋值并更新视图。和这里的问题有些像,我尝试了用set方法赋值:
this.$set(this.editValidateForm,'workType','1');
问题解决了,这种方法应该是属于强制刷新
,针对我的这种情况是有效的
3.2 尝试2
从强制刷新
这个点出发,还有一个解决方法,也是vue API文档提供了vm.$forceUpdate()
方法:
迫使 Vue 实例重新渲染。注意它仅仅影响实例本身和插入插槽内容的子组件,而不是所有子组件。
我们赋值照旧,但是在select的change函数里加上这个方法:
open(){
..
this.editValidateForm.worktType = 1
}
handleChange(){
this.$forceUpdate();
}
同样解决了这个问题。
3.3 尝试3
在编辑时,因为这条数据已存在,点击数据请求数据信息,在success回调里这样对数据进行赋值:
requestApi(params).then(function(res) {
this.editValidateForm = res.data;
})
在这种情况下,是没有这类bug的。由此联想即使目标只是给workType这一项重新赋值,也要给整个this.editValidateForm对象赋值:
this.editValidateForm = {
...
workType:"1"
}
问题解决。但这样比较繁琐,为了一个属性,就要重新赋值整个对象。
3.4 尝试4
由尝试3,同样是在dialog里,但是给表单中的select组件绑定一个data子属性级的数据,也就是说将绑定的workType移到与editValidateForm同级,也能解决这个问题:
<el-form-item label="类型:">
<el-select v-model="workType"
placeholder="请选择类型">
<el-option v-for="(item,index) in types"
:key="index"
:label="item.name"
:value="item.value">
{{item.name}}
</el-option>
</el-select>
</el-form-item>
data(){
return{
workType:'',
editValidateForm:{
...
}
}
},
3.5 尝试5
因为这是改别人的代码,后来发现虽然上面的方法确实能解决我的问题,但我这里视图不更新真正的原因是上面这行代码,去掉后就不存在这个问题了:
methods:{
open(){
this.editValidateForm = {}; //问题所在
this.editValidateForm.worktType = 1
},
}
思考了下,觉得应该是这样:先将表单对象editValidateForm置为空对象,那这个对象之前所有的属性包括worktType 都不存在了,之后再通过“.”这样的方式对一个不存在的对象属性进行赋值。这样一来就不是尝试1里说的情况相似,而是就是同样的问题:
https://cn.vuejs.org/v2/guide/list.html
数组更新检测
注意事项
由于 JavaScript 的限制,Vue 不能检测以下变动的数组:
当你利用索引直接设置一个项时,例如:vm.items[indexOfItem] = newValue
对象更改检测注意事项
还是由于 JavaScript 的限制,Vue 不能检测对象属性的添加或删除:
对于已经创建的实例,Vue 不能动态添加根级别的响应式属性。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式属性
所以尝试3能解决问题的根源也在于它是对已存在的
editValidateForm对象赋值
在vue里,当你对一个不存在的属性或对象直接“.”进行赋值,或者对数组不存在的索引项直接用索引赋值,从控制台打印可以看到数据变更,但无法使视图更新
所以,即使这个form不在dialog里,这样赋值也会不更新视图。遇到这种情况都可以用上面的方法试试