Vue实现数组四级联动

前言

最近项目上有个需求就是作下拉列表的四级联动,使用的是vuejs + elementui,使用数组存储对象的形式作为列表渲染到页面上的数据,可是在下拉列表联动的时候发现几个问题,如今记录下解决办法,分享给你们。html

  1. 修改对象数组后前端页面不从新渲染
  2. 查看或者编辑回显数据时,联动数据渲染出错(只显示key,不显示name)

image.png

关于复杂数据处理

以前在写React的时候,复杂一点的数据会经过Immutable.js来实现,经过get和set来实现数据的设置和读取,以及深层拷贝等功能,如今到了Vue发现数据复杂一点就不知道如何处理,第三方关于vue的immutable.js框架也没有了解过,后面有时间能够关注并学习下(你们有使用过的能够分享给我)。前端

四级联动问题解决方法

  • 问题一:修改对象数组后前端页面不从新渲染

这个问题其实Vue官网也说明过关于数组变化不会从新渲染页面的问题。vue

Vue 不能检测如下数组的变更:react

当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
当你修改数组的长度时,例如:vm.items.length = newLength
举个例子:segmentfault

var vm = new Vue({
   data: {
     items: ['a', 'b', 'c']
   }
 })
vm.items[1] = 'x' // 不是响应性的
vm.items.length = 2 // 不是响应性的
为了解决第一类问题,如下两种方式均可以实现和 vm.items[indexOfItem] = newValue 相同的效果,同时也将在响应式系统内触发状态更新:
// Vue.set
 Vue.set(vm.items, indexOfItem, newValue)
 // Array.prototype.splice
 vm.items.splice(indexOfItem, 1, newValue)
你也可使用 vm.$set 实例方法,该方法是全局方法 Vue.set 的一个别名:
vm.$set(vm.items, indexOfItem, newValue)
为了解决第二类问题,你可使用 splice:
vm.items.splice(newLength)

所以解决办法就是代码里使用Vue.set(vm.items, indexOfItem, newValue),下面就演示个例子:数组

export default {
    data(){
        return {
            arrys :[
                {
                    one: '',
                    oneList: Promise: getOneList(),
                    two: '',
                    twoList: Promise: getTwoList(one),
                    three: '',
                    threeList: Promise: getThreeList(two),
                    four: '',
                    fourList: Promise: getFourList(three),
                }
            ]
        }
    },
    methods: {
        // one下拉列表change事件
        oneChange(key, index){
            this.getTwoList(key).then(res => {
            this.arrys.forEach((item, i) => {
              if (i === index) {
                // 由于是四级联动,因此change one以后,two、three和four都要置空
                let newitem = {
                  two: [],
                  twoList: res,
                  three: '',
                  four: '',
                  threeList: [],
                  fourList: []
                }
                // 说明:修改arrys中第i个的数据,只有使用Vue.set页面才会从新渲染
                // newitem会覆盖item中的数据,并生成一个新的引用指针
                Vue.set(this.arrys, i, Object.assign({}, item, newitem))
              }
            })
          });
        },
        // two下拉列表change事件
        twoChange(key, index){
            
        },
        // three下拉列表change事件
        threeChange(key, index){
            
        },
        // four下拉列表change事件
        fourChange(key, index){
            
        },
        // 获取one 列表
        getOneList(){
            
        },
        // 获取two 列表
        getTwoList(oneKey){
            
        },
        // 获取three 列表
        getThreeList(twoKey){
            
        },
        // 获取four 列表
        getFourList(threeKey){
            
        }
    }
    
}

按照上面的代码就能够实现四级联动及change的时候页面可以动态渲染,这样就完成了联动效果以及修改对象数组后前端页面不从新渲染问题了。promise

  • 问题二:查看或者编辑回显数据时,联动数据渲染出错(只显示key,不显示name)

这个问题是这样的:咱们保存到后台数据one、two、three和four,而oneList、twoList、threeList和fourList不用保存(经过另外接口获取,并每次打开的时候都去调用),以后咱们查看和编辑上一次的四级联动的时候,咱们发现下拉列表中one、two、three和four只显示key,不显示name,缘由就在于oneList、twoList、threeList和fourList比one、two、three和four数据赋值时要“慢”,由于是异步的关系,因此当list回调回来的时候,页面已经渲染了,因此不成功,所以就出现了问题二:只显示Key,不显示name的问题。框架

那么如何解决这慢的问题呢?咱们可使用Promise.all来解决。异步

// 假设res是后台返回的要渲染到页面上的四级联动数组数据
let resdata = res;

// 给one、two、three和four赋值
resdata.forEach(item => {
    this.arrys.push({
        one: item.one,
        two: item.two,
        three: item.three,
        four: item.four
    })
})

// 获取twoList(说明:由于oneList是首级,因此直接获取就好,这里就不展现代码了)
Promise.all(resdata.map(item => this.getTwoList(item.one)))
    .then(twoListData => {
    
        twoListData.forEach((data, i) => {
            
            this.arrys[i].twoList = data;
    
        })
})
  
// promise获取threeList列表
Promise.all(resdata.map(item => this.getThreeList(item.two)))
  .then(threeListData => {
  
        threeListData.forEach((data, i) => {
        
            this.arrys[i].threeList = data;
            
        })

  })
  
// promise获取fourList列表
Promise.all(resdata.map(item => this.getFourList(item.three)))
  .then(fourListData => {
  
        fourListData.forEach((data, i) => {
        
            this.arrys[i].fourList = data;
            
        })

  })

为何要写三次Promise.all?由于forEach是异常的,因此不能在forEach里面循环获取Promise来给arrys赋值,若是你们有更好的方法能够提出来。ide

这样就解决了第二个问题。

总结

一、可能有人会问:为何不把oneList和twoList设置成公共的列表,和arrys数组分开,这样不是更方便读取吗?答案是:不能,由于是四级联动,因此只能每一个对象里面保存一份oneList和twoList,设想一下:若是arrys数组里面有三条数据,我改变了第一条的one,那么twoList就会变化,而第二条的twoList也就跟着变了,这就是否是单独的四级联动了,而是全部twoList都跟着动了!

二、el-select只要单独赋值key和list,就能显示对应的name(说明当key赋值上去的时候,el-select的list就去找对应的,找到了就显示出名称name)

三、作的过程当中发现有个问题:change的时候发现two和three还有four只显示key,不显示name,后来发现是由于使用了ht-select而没有用elementUI自带的el-select,换成以后就没问题了,也算一个小插曲吧。

引用

学习使用Promise