3

Preface

Recently, there is a requirement on the project to do the four-level linkage of the drop-down list, using vuejs + elementui, using the form of array storage object as the data that the list is rendered on the page, but several problems were found when the drop-down list was linked. Now record the solution and share it with everyone.

  1. The front-end page does not re-render after modifying the object array
  2. When viewing or editing the echo data, the linkage data rendering error (only the key is displayed, not the name)

image.png

About complex data processing

When writing React before, the more complex data will be realized through Immutable.js , and the data setting and reading through get and set, as well as deep copy and other functions, now come to Vue and find that the data is a bit more complex. I don’t know How to deal with it, the third party hasn't learned about vue's immutable.js framework, and I have time to follow and learn later (you can share with me if you have used it).

Four-level linkage problem solution

  • Problem 1: The front-end page does not re-render after modifying the object array

This question actually Vue official website also explained the problem that the page will not be re-rendered when the array changes.

Vue cannot detect changes in the following arrays:

When you use the index to directly set an array item, for example: vm.items[indexOfItem] = newValue
When you modify the length of the array, for example: vm.items.length = newLength
for example:

 var vm = new Vue({
   data: {
     items: ['a', 'b', 'c']
   }
 })
vm.items[1] ='x' // not responsive
vm.items.length = 2 // not responsive
In order to solve the first type of problem, the following two methods can achieve the same effect as vm.items[indexOfItem] = newValue, and will also trigger a status update in the responsive system:
 // Vue.set
 Vue.set(vm.items, indexOfItem, newValue)
 // Array.prototype.splice
 vm.items.splice(indexOfItem, 1, newValue)
You can also use the vm.$set instance method, which is an alias for the global method Vue.set:
 vm.$set(vm.items, indexOfItem, newValue)
To solve the second type of problem, you can use splice:
 vm.items.splice(newLength)

So the solution is to use Vue.set(vm.items, indexOfItem, newValue) code. Here is an example:

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){
            
        }
    }
    
}

According to the above code, the four-level linkage and the page can be dynamically rendered when changing, so that the linkage effect is completed and the front-end page does not re-render after the object array is modified.

  • Problem 2: When viewing or editing the echo data, the linkage data rendering error (only the key is displayed, not the name)

The problem is this: we save to the background data one, two, three and four, but oneList, twoList, threeList and fourList do not need to be saved (obtained through another interface, and called every time it is opened), and then we check and When editing the last four-level linkage, we found that one, two, three, and four in the drop-down list only display the key, but not the name. The reason is that oneList, twoList, threeList, and fourList are more than one, two, three, and four data assignments. When the time is "slow", because it is asynchronous, so when the list callback comes back, the page has been rendered, so it is unsuccessful, so there is a second problem: only the key is displayed, but the name is not displayed.

So how to solve this slow problem? We can use Promise.all to solve.

// 假设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;
            
        })

  })

Why write Promise.all three times? Because forEach is abnormal, you can't get Promises in forEach to assign values to arrys. If you have a better method, you can come up with it.

This solves the second problem.

Summarize

1. Someone may ask: Why not set oneList and twoList as a public list, separate from the arrys array, isn't it more convenient to read? The answer is: No, because it is a four-level linkage array, each object in the array should store its own oneList and twoList. Imagine: if there are three data in the arrys array, I change the first one, then twoList It will change, and the second twoList will also change. This is not a single four-level linkage, but all the twoLists will follow!

2. As long as el-select assigns the key and list separately, the corresponding name can be displayed (indicating that when the key is assigned, the list of el-select will find the corresponding, and the name will be displayed if it is found)

3. A problem was discovered during the process: when changing, it was found that two, three and four only displayed the key but not the name. Later, it was discovered that the el-select that comes with elementUI was not used because of the use of ht-select. After it's finished, it's okay, and it's a small episode.

Quote

Learn to use Promise


Awbeci
3.1k 声望213 粉丝

Awbeci