Home > Software engineering >  VUE undefined error after splice the last element
VUE undefined error after splice the last element

Time:09-28

This is simplified version of bigger component I'm working on. Please help me to solve the mystery - why I'm getting error on deleting (splicing) the last item from the list ?

  • If you delete one by one - no error. It happens, only when the last item is deleted and at least one item is in front of it.
  • If @click="handleProductLineTap(line, index)" is removed at v-for - no error.

    <template>
      <div v-for="(row, index) in this.rows" :key="index" v-bind:id="`row-${index}`"  @click="handleProductrowTap(index)">
        <div v-bind:id="`row-${index}`" v-bind:>
          <span>{{ row.text }}</span> <button  style="background-color: white; border:black;" @click.prevent="deleteRow(row, index)">Delete me </button>
        </div>
      </div>
    </template>
    
    <script>
    export default {
      name: 'TestRow',
      created() {},
      data() {
        return {
          rows: [
            {'id':1, text: 'Granny smith', type: 'apple'},
            {'id':2, text: 'Conference', type: 'pear'},
            {'id':3, text: 'Alpine', type: 'strawberry'},
          ],
          currentRowIndex: 0
        }
      },
      methods: {
        //returns css styling to every row in v-for. 
        //It sets color of the row according to the row.type and highlights selected row (if currentRowIndex == index). 
        rowCssDecorations(row) {
          var resultingCss = ''
    
          //change color according to rows.type
          switch (row.type) {
            case 'apple':
              resultingCss = resultingCss   'appleClass'
              break
            case 'pear':
              resultingCss = resultingCss   'pearClass'
              break
            case 'strawberry':
              resultingCss = resultingCss   'strawberryClass'
              break
    
            default:
              resultingCss = resultingCss   'defaultClass'
              break
          }
    
          //Hliglight the current row
          if (this.rows[this.currentRowIndex].id == row.id) {
            resultingCss = resultingCss   ' borderSelected '
            } else {
              resultingCss = resultingCss   ' borderNormal  '
            }
    
          return resultingCss
        },
    
        //deletes the row
        deleteRow(index) {
          this.rows.splice(index, 1)
        },
    
        //Simply sets the selected row id to currentRowIndex  .
        handleProductrowTap( index) {
          this.currentRowIndex = index
        },
      },
    }
    </script>
    <style lang="scss" scoped>
    .appleClass {
      background-color: greenyellow;
    }
    .pearClass {
      background-color: palegoldenrod;
    }
    .strawberryClass {
      background-color: red;
    }
    .defaultClass {
      background-color: gainsboro;
    }
    .borderSelected{
      border-color: black;
      border-width: 4px;
    }
    .borderNormal{
      border-color: black;
      border-width: 2px;
    }
    </style>

CodePudding user response:

After looking into the fiddle link you shared, I came up with below observations/root cause :

  • You are calling deleteRow method with two parameters (row, index) but in the method definition you are accepting only index which causing the issue in delete the record.

    To resolve this you have to just pass the index in deleteRow method from the template.

  • Another observation is that as you are passing row and index both in rowCssDecorations method. Hence, no need of currentRowIndex, you can directly use the index in rowCssDecorations method.

Live Demo :

new Vue({
  el: '#app',
  data: {
    rows: [{
        'id': 1,
        text: 'Granny smith',
        type: 'apple'
      },
      {
        'id': 2,
        text: 'Conference',
        type: 'pear'
      },
      {
        'id': 3,
        text: 'Alpine',
        type: 'strawberry'
      }
    ],
    currentRowIndex: 0
  },
  methods: {
    deleteRow(index) {
      this.rows.splice(index, 1)
    },
    
    rowCssDecorations(row, index) {
      var resultingCss = ''

      //change color according to rows.type
      switch (row.type) {
        case 'apple':
          resultingCss = resultingCss   'appleClass'
          break
        case 'pear':
          resultingCss = resultingCss   'pearClass'
          break
        case 'strawberry':
          resultingCss = resultingCss   'strawberryClass'
          break

        default:
          resultingCss = resultingCss   'defaultClass'
          break
      }

      //Hliglight the current row
      if (this.rows[index].id == row.id) {
        resultingCss = resultingCss   ' borderSelected '
      } else {
        resultingCss = resultingCss   ' borderNormal  '
      }

      return resultingCss
    },

    //deletes the row
    deleteRow(index) {
      this.rows.splice(index, 1)
    }
  }
})
 .appleClass {
   background-color: greenyellow;
 }

 .pearClass {
   background-color: palegoldenrod;
 }

 .strawberryClass {
   background-color: red;
 }

 .defaultClass {
   background-color: gainsboro;
 }

 .borderSelected {
   border-color: black;
   border-width: 4px;
 }

 .borderNormal {
   border-color: black;
   border-width: 2px;
 }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div v-for="(row, index) in this.rows" :key="index" v-bind:id="`row-${index}`">
    <div v-bind:id="`row-${index}`" v-bind:>
      <span>{{ row.text }}</span> <button  @click.prevent="deleteRow(index)">Delete me</button>
    </div>
  </div>
</div>

CodePudding user response:

You should use @click.stop instead of @click.prevent

  • Related