oliverbj
oliverbj

Reputation: 6052

Deleting specific component in v-for array

I have below array, that contains a number of columns. Below example contains three columns, but columns can be added/removed dynamically:

[['position', '30'], ['position', '60'], ['position', '90']]

I am facing issues when deleting the correct column (index in array) with Vue.

Consider below snippet:

new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js!',
    columns: [['position', '30'], ['position', '60'], ['position', '90']]
  },
  methods: {
  	deleteColumn: function(index) {
    	this.columns.splice(index, 1);
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  
  <div v-for="(item, index) in columns" :key="index">
  Column #: {{index}} - <a @click="deleteColumn(index)">Delete me</a>
  
  </div>
  
</div>

If you run the above code snippet end try to delete the #1 column, it will actually remove the #2 column (last item of the array). Same goes for #0.

I thought that by providing the index to my deleteColumn function, I could remove the "right" index from the array.

Any help is appreciated.

Upvotes: 2

Views: 1480

Answers (5)

Mexxx
Mexxx

Reputation: 61

Try this this.$delete(this.columns, index) which is the same as Vue.delete(this.columns, index)

https://v2.vuejs.org/v2/api/index.html#Vue-delete

Upvotes: -1

Hamid
Hamid

Reputation: 81

You should not use index as the key with CRUD operations since this will confuse Vue when it comes to deleting. The key should be a unique identifier that relates to the data.

You can create a new formatted array of objects on mount with a key generated from the data within the array (note: I haven't tested the code in a browser if there are any mistakes).

<template>
  <div>
    <div v-for="col in formattedColumns" :key="col.key">
      {{ col.value }}
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      columns: [['position', '30'], ['position', '60'], ['position', '90']],
      formattedColumns: null,
    };
  },
  mounted() {
    let columns = [];
    for (let i = 0; i < this.columns.length; i++) {
      columns.push({
        value: this.columns[i],
        key: this.columns[i][0] + this.columns[i][1],
      });
    }

    this.formattedColumns = columns;
  },
};
</script>

Upvotes: 0

weegee
weegee

Reputation: 3409

Just give them a property name and you are done. Notice what I changed here. Columns is no more a 2D array, but objects. Use this.$delete(this.columns, index); to delete the objects.

new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js!',
    columns: {
      '1': {
        position: 30
      },
      '2': {
        position: 60
      },
      '3': {
        position: 90
      }
    }
  },
  methods: {
    deleteColumn: function(index) {
      this.$delete(this.columns, index);
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">

  <div v-for="(item, index) in columns" :key="index">
    Column #: {{index}} - <a @click="deleteColumn(index)">Delete me</a>

  </div>

</div>

{
  '1': {
    position: 30
  },
  '2': {
    position: 60
  },
  '3': {
    position: 90
  }
}

Here, '1' is a property name and it's value is another object. It's like giving ids to your data.

The format for value of object is this

{ property_name : value }

Here, value is another object, and in that object, there is another property, named "position" with your corresponding values.

Upvotes: 2

Ilmari Karonen
Ilmari Karonen

Reputation: 50368

The splice method reindexes the array, moving all elements after the splice point up or down so that any new inserted values will fit and so that the array indices remain contiguous. You can see it more clearly if you also display the values of the items in your list:

new Vue({
  el: '#app',
  data: {
    message: 'Hello Vue.js!',
    columns: ['foo', 'bar', 'baz']
  },
  methods: {
    deleteColumn: function(index) {
      this.columns.splice(index, 1);
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div v-for="(item, index) in columns" :key="index">
  Column #{{index}} = {{item}} - <a @click="deleteColumn(index)" style="cursor:pointer">Delete me</a>      
  </div>
</div>

Initially, the snippet above will render like this:

Column #0 = foo - Delete me
Column #1 = bar - Delete me
Column #2 = baz - Delete me

If you now click the "Delete me" link on column #0 ("foo"), it will change to:

Column #0 = bar - Delete me
Column #1 = baz - Delete me

You can see that the value "foo" indeed got spliced out of the array — and the values "bar" and "baz" were shifted down by one position to become the new elements #0 and #1.


Anyway, the fix for this problem is simply "don't do that":

  • If you're using v-for with a simple array whose elements have no natural key value, you can just omit :key entirely and let Vue decide how to best handle changes to the underlying array. As long as the contents within the v-for loop doesn't contain any form inputs or stateful components or other fancy stuff that doesn't react well to the array being reindexed, it should work just fine.

  • Conversely, if you do have a natural unique key available for each array element, use it. If you don't, but can create one, consider doing that.

Upvotes: 0

When you clicked any item you are removing it in the right way, your index is your key, that's the problem, but is visually, in the logic it's right. Display your position in your template just for you can see it. ANd for me your data it's not in the right way.

<div id="app">
     <div v-for="(item, index) in columns" :key="index">
         Column #: {{index}}-{{item.position}} - 
         <a @click="deleteColumn(index)">Delete me</a>
     </div>  
</div>

and your script for you can see the change

new Vue({
 el: '#app',
 data: {
  message: 'Hello Vue.js!',
  columns: [{position: 30}, {position: 60}, {position: 90}]
 },
 methods: {
  deleteColumn: function(index) {     
    this.columns.splice(index, 1);
  }
 }
})

Upvotes: 1

Related Questions