'Vue.js - Remove specific component dynamically

I am trying to dynamically create/remove a Vue component. I have figured out how to dynamically add the component, but I am having some troubles with allowing the users to remove the specific component.

Consider below two Vue files:

TableControls.vue

<a v-on:click="addColumn">Add Column</a>

<script>
    export default {
        methods: {
            addColumn: function () {
                Event.$emit('column-was-added')
            }
        }
    };

</script>

DocumentViewer.vue:

<div v-for="count in columns">
   <VueDragResize :id="count">
      <a @click="removeColumn(count)">Remove Column</a>
   </VueDragResize>
</div>

<script>
import VueDragResize from 'vue-drag-resize';

    export default {
        components: {
            VueDragResize
        },
        data() {
            return {
                columns: [1],
            }

        },
        created() {
            Event.$on("column-was-added", () => this.addColumn())
        },

        methods: {
            addColumn: function () {
                this.columns.push(this.columns.length + 1)
            },
            removeColumn: function (id) {
                this.columns.splice(id, 1)
            }
        }
    };
</script>

As you can see, whenever a user clicks on <a v-on:click="addColumn">Add Column</a>, it will submit an event, and the DocumentViewer.vue file will pick up it, firing the addColumn method. This will ultimately create a new <VueDragResize></VueDragResize> component.

This works great.

The problem is when I want to remove the component again. My removeColumn method simply removes an id from the columns array:

removeColumn: function (id) {
    this.columns.splice(id, 1)
}

This results in that a column is in fact removed. However, consider below example. When user clicks on the remove icon for the first column, it will remove the 2nd column instead. (And when there is only one column present, it cannot be removed).

Vue Example

I believe this is due to the fact that I splice() the array, but I cannot see how else I can remove the component dynamically?



Solution 1:[1]

I see, Array on Vue does not re render when you modify them.

You need to use the

Vue.set(items, indexOfItem, newValue)

if you want to modify

and use

Vue.delete(target, indexOfObjectToDelete);

If you want to delete an item from an array

You may read the additional info here https://vuejs.org/v2/api/#Vue-delete

If you want to delete an item from array. Using this will cause the component to rerender.

In this case it will be intuitive to do this

removeColumn: function (id) {
   Vue.delete(this.columns, id)
}

Note that id should be the index. Vue.delete ensures the re-render of the component.

EDIT, you must use the index, instead of the count here.

<div v-for="(count, index) in columns">
   <VueDragResize :id="index">
      <a @click="removeColumn(index)">Remove Column</a>
   </VueDragResize>
</div>

Solution 2:[2]

I would recommend reshaping your data, each element should be an object with an id and whatever other properties you want. Not simply an id then you would need something like this:

removeColumn(id) {
                const elToRemove = this.columns.findIndex(el => el.id === id)
                let newArr = [elToRemove, ...this.columns]
                this.columns = newArr
            }

Also make another computed property for columns like this to make sure they change dynamically (when you add/remove):

computed: {
      dynColumns(){ return this.columns}
    }

Solution 3:[3]

I have same problem, and I found the solution of this problem. It is need to set #key with v-for. This is Built-in Special Attributes.

By default, if you do not set "#key", array index is set to#key. So if array length is 3, #key is 0,1,2. Vue identify eash v-for elements by key. If you remove second value of array, then array index is 0 and 1, because array length is 2. Then Vue understand that #key==2 element removed, So Vue remove 3rd component. So if you remove second value of array, if no #key, third component will be removed. To avoid this, need to set #key to identify component like this:

let arr = [
  { id: 'a', ...},
  { id: 'b', ...},
  { id: 'c', ...}
];

<div v-for="obj in arr" :key="obj.id">
  <someYourComponent>
    ...
  </someYourComponent>
</div>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 oliverbj
Solution 2 Michael
Solution 3