How to save sorted arrays and components in Vue.js

I use Vue.js and Dragula to create a drag and drop page. Each tile contains its own dataset, so each tile is a Vue component.

The problem is that as soon as I drag one of the fragments, the DOM elements and the data array in the Vue instance fall out of sync and start causing problems. Just dragging and dropping does not create a problem, but as soon as I drag and drop something and then try to delete it, everything goes wrong.

Here's the script: https://jsfiddle.net/wfjawvnL/13/

Here is my HTML, with the component template:

<body> <div id="app"> <div id="draggable" class="wrapper"> <template v-for="(index, item) in list"> <block :id="index" :index="index" :item="item" :name="item.name"> </block> </template> </div> <div class="footer"> <pre>{{ $data | json }}</pre> </div> </div> </body> <template id="block-template"> <div :id="index" :class="[name, 'block']"> <div class="text name">{{ name }}</div> <div>Index: {{ index }}</div> <span class="delete" v-on:click="removeItem(item)">&#x2717;</span> </div> </template> 

Here is my Vue instance:

 var vm = new Vue({ el: '#app', data: { list: [ {name: 'item1'}, {name: 'item2'}, {name: 'item3'}, {name: 'item4'}, {name: 'item5'} ] }, methods: { reorder: function (element, sibling) { var children = element.parentNode.querySelectorAll(".block"); var length = this.list.length; var ids = []; for (var i = 0; i < length; i++) { if (children[i].id) { ids.push(children[i].id); } children[i].id = i; } var vm = this; var newArr = ids.map(function (id) { return vm.list[id]; }); this.list = newArr; } } }); 

Here's the component:

 Vue.component('block', { template: '#block-template', props: ['index', 'name', 'item'], methods: { removeItem: function (item) { vm.list.$remove(item); } } }); 

And I call Dragula as follows:

 dragula([document.getElementById('draggable')]).on('drop', function (el, target, source, sibling) { vm.reorder(el, sibling); }); 

This fiddle works fine, but does not use components: https://jsfiddle.net/z2s83yfL/1/

+5
source share
1 answer

I had two problems. First, I linked the index of the array and used it as id. The index changed, which also led to a change in identifier. Secondly, using v-for in the template tag caused some problems. I changed the tags to divs and now it works.

Working fiddle: https://jsfiddle.net/wfjawvnL/18/

Vue Material:

 Vue.component('block', { template: '#block-template', props: ['name', 'item'], methods: { removeItem: function (item) { vm.list.$remove(item); } } }); var vm = new Vue({ el: '#app', data: { list: [ {name: 'item1'}, {name: 'item2'}, {name: 'item3'}, {name: 'item4'}, {name: 'item5'} ] }, ready: function() { var self = this; var from = null; var drake = dragula([document.querySelector('#draggable')]); drake.on('drag', function(element, source) { var index = [].indexOf.call(element.parentNode.children, element); from = index; }); drake.on('drop', function(element, target, source, sibling) { var index = [].indexOf.call(element.parentNode.children, element); self.list.splice(index, 0, self.list.splice(from, 1)[0]); }); } }); 

HTML Material:

 <body> <div id="app"> <div id="draggable" class="wrapper"> <div class="wrapper" v-for="item in list"> <block :item="item" :name="item.name"> </block> </div> </div> <pre>{{ $data | json }}</pre> </div> </body> <template id="block-template"> <div :class="[name, 'block']"> <div class="text name" v-on:click="removeItem(item)">{{ name }}</div> <span class="delete" v-on:click="removeItem(item)">&#x2717;</span> </div> </template> 
+1
source

Source: https://habr.com/ru/post/1245021/


All Articles