带有 Computed 属性 的 Vue3 For-Loop 立即触发 onDragEnd
Vue3 For-Loop with Computed Property triggers onDragEnd immediately
我正在尝试为表单生成器开发我自己的简单拖放功能。我有一个项目列表,它在一个 v-for 循环中呈现,计算 属性。在 onDragStart 上,我设置了状态 isDragging。这应该会改变计算 属性 中的列表,并插入一些我可以放置拖动项的插槽。但是在拖动一个item之后,它立即触发了onDragEnd事件。
最初我缺少 :key 属性,但添加它并没有解决问题。希望有人知道解决方案或知道哪里出了问题。
这里是一个Link代码:https://jsfiddle.net/elrihor/vwb3k6qc/49/
Vue.createApp({
data() {
return {
list: [{
id: 'i1',
name: 'Apfel',
},
{
id: 'i2',
name: 'Banane',
},
{
id: 'i3',
name: 'Kirsche',
},
{
id: 'i4',
name: 'Orange',
},
],
props: {
item: {
props: {
draggable: true,
ondragstart: ($event) => {
this.startDrag();
},
ondragend: ($event) => {
this.endDrag();
},
},
},
slot: {
props: {
ondrop: ($event) => {
$event.preventDefault();
this.onDrop($event);
},
ondragover: ($event) => {
$event.preventDefault();
}
},
},
},
isDragging: false
}
},
computed: {
dragList() {
let dragList = this.list;
if (this.isDragging) {
dragList = dragList.reduce((r, a) => r.concat(a, {
type: 'slot',
name: 'Slot'
}), [{
type: 'slot',
name: 'Slot'
}]);
}
dragList = dragList.map((item, index) => {
return {
...item,
id: !item.id ? 's' + index : item.id,
type: item.type == 'slot' ? 'slot' : 'item',
};
});
return dragList;
}
},
methods: {
getProps(type) {
return this.props[type].props;
},
startDrag() {
console.log('start');
this.isDragging = true;
},
endDrag() {
console.log('end');
this.isDragging = false;
},
onDrop(e) {
console.log('drop');
}
}
}).mount('#app')
我认为问题在于选择使用 ondragstart
事件。当它被触发时, DOM 是 re-rendered 来显示你的插槽,它会四处移动,这样你的鼠标光标就不再位于元素本身上。 dragend
然后在拖动实际开始之前被触发。只需将其更改为 ondrag
即可:
https://jsfiddle.net/m7ps3f40/
为了调试和解决这个问题,我大大简化了您的 fiddle。我觉得 dragList
计算和 :bind="getProps"
使用了更多的代码,并且与仅使用 HTML 元素和 Vue-style 事件进行拖动的更简单方法相比,可读性更差。看看你可能会觉得很有趣:
https://jsfiddle.net/o5pyhtd3/2/
当然,您的 fiddle 可能是一段较大代码的缩减版本,因此您可能需要坚持自己的方法。
我正在尝试为表单生成器开发我自己的简单拖放功能。我有一个项目列表,它在一个 v-for 循环中呈现,计算 属性。在 onDragStart 上,我设置了状态 isDragging。这应该会改变计算 属性 中的列表,并插入一些我可以放置拖动项的插槽。但是在拖动一个item之后,它立即触发了onDragEnd事件。
最初我缺少 :key 属性,但添加它并没有解决问题。希望有人知道解决方案或知道哪里出了问题。
这里是一个Link代码:https://jsfiddle.net/elrihor/vwb3k6qc/49/
Vue.createApp({
data() {
return {
list: [{
id: 'i1',
name: 'Apfel',
},
{
id: 'i2',
name: 'Banane',
},
{
id: 'i3',
name: 'Kirsche',
},
{
id: 'i4',
name: 'Orange',
},
],
props: {
item: {
props: {
draggable: true,
ondragstart: ($event) => {
this.startDrag();
},
ondragend: ($event) => {
this.endDrag();
},
},
},
slot: {
props: {
ondrop: ($event) => {
$event.preventDefault();
this.onDrop($event);
},
ondragover: ($event) => {
$event.preventDefault();
}
},
},
},
isDragging: false
}
},
computed: {
dragList() {
let dragList = this.list;
if (this.isDragging) {
dragList = dragList.reduce((r, a) => r.concat(a, {
type: 'slot',
name: 'Slot'
}), [{
type: 'slot',
name: 'Slot'
}]);
}
dragList = dragList.map((item, index) => {
return {
...item,
id: !item.id ? 's' + index : item.id,
type: item.type == 'slot' ? 'slot' : 'item',
};
});
return dragList;
}
},
methods: {
getProps(type) {
return this.props[type].props;
},
startDrag() {
console.log('start');
this.isDragging = true;
},
endDrag() {
console.log('end');
this.isDragging = false;
},
onDrop(e) {
console.log('drop');
}
}
}).mount('#app')
我认为问题在于选择使用 ondragstart
事件。当它被触发时, DOM 是 re-rendered 来显示你的插槽,它会四处移动,这样你的鼠标光标就不再位于元素本身上。 dragend
然后在拖动实际开始之前被触发。只需将其更改为 ondrag
即可:
https://jsfiddle.net/m7ps3f40/
为了调试和解决这个问题,我大大简化了您的 fiddle。我觉得 dragList
计算和 :bind="getProps"
使用了更多的代码,并且与仅使用 HTML 元素和 Vue-style 事件进行拖动的更简单方法相比,可读性更差。看看你可能会觉得很有趣:
https://jsfiddle.net/o5pyhtd3/2/
当然,您的 fiddle 可能是一段较大代码的缩减版本,因此您可能需要坚持自己的方法。