sortable.js
是一款很好用的拖拽插件,最近尝试用它做一个列表卡片拖拽功能。
我是通过vue
搭的项目,但是在实际显示中却还是有些问题。
话不多说上代码:
<!-- template -->
<template>
<div>
<ul ref="con">
<li v-for="item in renderList1">{{item.caption}}</li>
</ul>
<ul ref="con">
<li v-for="item in renderList2">{{item.caption}}</li>
</ul>
</div>
</template>
<!-- js -->
<script>
export default {
data: function(){
return {
renderList1: [
{caption: 'item1'},
{caption: 'item2'},
{caption: 'item3'},
{caption: 'item4'},
],
renderList2: [
{caption: 'item-a'},
{caption: 'item-b'},
{caption: 'item-c'},
{caption: 'item-d'},
],
sortList: []
}
},
mounted: function(){
this.init();
},
methods: {
init: function(){
this.$refs.con.forEach((item,index)=>{
this.sortList = Sortable.create(item,{
group: {
name: 'group'+index,
put: ['group1','group2']
},
animate: 150,
onEnd: function(evt){
// TODO
console.log(JSON.stringify(this.renderList1));
console.log(JSON.stringify(this.renderList2));
}
})
})
}
}
}
</script>
以上代码比较简单,仅仅拖拽的话,是能在视图上看到效果了,但是,当我们将其中一个容器中的item拖拽到另一个容器中时, 页面发生了变化,但是数据并未发生变化。
[{caption: 'item1'},{caption: 'item2'},{caption: 'item3'},{caption: 'item4'},]
[{caption: 'item-a'},{caption: 'item-b'},{caption: 'item-c'},{caption: 'item-d'}]
我想了想觉得显示没有问题, sortable本身是在操作DOM,并没有对数据进行任何处理。 或许你想到这里可能会说, 那就在onEnd的函数中 手动修改一下数据不就好了吗。
onEnd: (evt)=>{
let [fbox, oldIndex, tbox, newIndex, item] = [evt.form, evt.oldIndex, evt.to, evt.newIndex, evt.item];
var item = this.renderList1.splice(oldIndex,1); // 删掉第一个数组中对应的原位置数据
this.renderList2.splice(newIndex,0,item); //将刚刚删除的数据添加到第二个数组相应的位置
console.log(JSON.stringify(this.renderList1));
console.log(JSON.stringify(this.renderList2));
}
这样看起应该就能实现 数据跟视图一起变化了(才怪)。于是我们假设 拖拽 item3
到 item-b
的位置,我们发现,控制台显示如下
[{caption: 'item1'},
{caption: 'item2'},
{caption: 'item4'},]
[{caption: 'item-a'},
{caption: 'item3'},
{caption: 'item-b'},
{caption: 'item-c'},
{caption: 'item-d'}]
欧克!数据显示正常,正在我以为一切结束的时候,视图却显示成了这样
item1 item-a
item2 item4
item3
item-b
item-c
item-d
明明拖拽了一个,但是却有两个同时移过去了。其实这不难理解,我们在end函数中,对数据进行了splice操作。vue监听到了数据变化,于是页面视图跟着发生变化,再加上sortable本身会操作一次DOM,而且在onEnd函数之前(所以item3
在item4
下面)。于是就出现了视图中这种情况。
此问题的解决方案主要有两个思路,
一, 阻止onEnd的默认行为(不将dom移动过去,而是通过对数据操作影响视图)
二, 在onEnd中用 vue监听不到的方式修改数组。
我这里提到的就是第一种解决方案——阻止拖拽本身的行为(其实阻止不了,是复位DOM)
onEnd: (evt)=>{
let [fbox, oldIndex, tbox, newIndex, item] = [evt.form, evt.oldIndex, evt.to, evt.newIndex, evt.item];
/*将sortable移动过去的DOM复位*/
tbox.removeChild(item); //删掉已移过去的dom
fbox.insertBefore(item, fbox.children[oldIndex]); //添加已移除的dom
/* 手动修改数据,影响视图 */
var item = this.renderList1.splice(oldIndex,1); // 删掉第一个数组中对应的原位置数据
this.renderList2.splice(newIndex,0,item); //将刚刚删除的数据添加到第二个数组相应的位置
console.log(JSON.stringify(this.renderList1));
console.log(JSON.stringify(this.renderList2));
}
此时我们的控制台显示正常,同时页面也表现正常了。 至于第二种方式我并未做尝试, 只知道在官网上尤大大有提过 arr[i] = variable
这种对数组直接赋值的操作可能会监听失败。有兴趣的可以自行尝试一下。