Janis Jakaitis
Janis Jakaitis

Reputation: 327

VUE undefined error after splice the last element

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 ?


    <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:class="rowCssDecorations(row, index)">
          <span>{{ row.text }}</span> <button class="borderNormal" 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>

Upvotes: 0

Views: 213

Answers (2)

Roh&#236;t J&#237;ndal
Roh&#236;t J&#237;ndal

Reputation: 27192

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:class="rowCssDecorations(row, index)">
      <span>{{ row.text }}</span> <button class="borderNormal" @click.prevent="deleteRow(index)">Delete me</button>
    </div>
  </div>
</div>

Upvotes: 1

IVO GELOV
IVO GELOV

Reputation: 14259

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

Upvotes: 0

Related Questions