3

Vue实现数组四级联动

 2 years ago
source link: https://segmentfault.com/a/1190000040513506
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

image.png

关于复杂数据处理

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

四级联动问题解决方法

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

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

Vue 不能检测以下数组的变动:

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

 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的时候页面能够动态渲染,这样就完成了联动效果以及修改对象数组后前端页面不重新渲染问题了。

  • 问题二:查看或者编辑回显数据时,联动数据渲染出错(只显示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赋值,如果大家有更好的方法可以提出来。

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

1、可能有人会问:为什么不把oneList和twoList设置成公共的列表,和arrys数组分开,这样不是更方便读取吗?答案是:不能,因为是四级联动数组,所以数组中每个对象应该保存一份自己的oneList和twoList,设想一下:如果arrys数组里面有三条数据,我改变了第一条的one,那么twoList就会变化,而第二条的twoList也就跟着变了,这就是不是单独的四级联动了,而是所有twoList都跟着动了!

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

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

学习使用Promise


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK