[Vuejs]-How to add a new numerical (string) key and shift the rest by +1 without breaking reactivity in Vuejs data

0👍

There are a few problems to address here.

Firstly, trying to use var dataObj = this.treeData; and then this.treeData = dataObj isn’t going to help. Both dataObj and this.treeData refer to the same object and that object has already been processed by Vue’s reactivity system. You could address the reactivity problems by creating a totally new object but just creating an alias to the existing object won’t help.

Instead of creating a new object I’ve chosen to use this.$set in my example. This isn’t necessary for most of the properties, only the new one added at the end really needs it. However, it would have been unnecessarily complicated to single out that one property given the algorithm I’ve chosen to use.

Another potential problem is ensuring all numbers are compared as numbers and not as strings. In your example you’re passing in the position as the string '2'. Operators such as < will give you the expected answer for numbers up to 9 but once the number of items in treeData reaches 10 you may start to run into problems. For string comparision '2' < '10' is false.

The next problem is the order you’re moving the entries. In your current algorithm you’re overwriting entry key + 1 with entry key. But that means you’ve lost the original value for entry key + 1. You’ll end up just copying the same entry all the way to the end. There are two ways you could fix this. One would be to use a new object to hold the output (which would also help to address the reactivity problem). In my solution below I’ve instead chosen to iterate backwards through the keys.

new Vue({
  el: '#app',
  
  data () {
    return {
      newEntry: 'Yellow',
      newIndex: 4,
      
      treeData: {
        1: 'Red',
        2: 'Green',
        3: 'Blue'
      }
    }
  },
  
  computed: {
    treeDataLength () {
      return Math.max(...Object.keys(this.treeData))
    }
  },
  
  methods: {
    onAddClick () {
      const newIndex = Math.round(this.newIndex)
      
      if (newIndex < 1 || newIndex > this.treeDataLength + 1) {
        return
      }

      this.injectFieldType(this.newEntry, newIndex)
    },
    
    injectFieldType (type, position) {
      const list = this.treeData
      
      for (let index = this.treeDataLength + 1; index >= position; --index) {
        if (index === position) {
          this.$set(list, index, type)
        } else {
          this.$set(list, index, list[index - 1])
        }
      }    
    }
  }
})
<script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
<div id="app">
  <ul>
    <li v-for="index in treeDataLength">
      {{ index}}. {{ treeData[index] }}
    </li>
  </ul>
  <input v-model="newEntry">
  <input v-model="newIndex">
  <button @click="onAddClick">Add</button>
</div>

The decision to use an object with number keys seems very strange. This would all be a lot easier if you just used an array.

Leave a comment