MaxLeiter / sortablejs-vue3

A thin wrapper around Sortable.js for Vue 3
https://sortablejs-vue3.maxleiter.com
MIT License
364 stars 20 forks source link

Button @click event is lost on Clone #108

Open marcopixel opened 3 months ago

marcopixel commented 3 months ago

Description

When attempting to move/clone a element from the left list to the right one the click event is lost because the original click event is on the "clone" inside the right list. The expected behavior is that all elements should retain all events on the elements inside.

Environment:

vite-vue-starter@0.0.0 /home/projects/vitejs-vite-9v4mkx
+-- @vitejs/plugin-vue@5.0.4
+-- sortablejs@1.15.2
+-- vite@5.2.8
+-- vue-draggable-plus@0.4.0
`-- vue@3.4.21

Browser: Chrome 123.0.6312.106

Repro Case

https://stackblitz.com/edit/vitejs-vite-ilyqms?file=src%2FApp.vue

Steps to Reproduce:

  1. Grab an element from the left box
  2. Try to drop it on the right box
  3. The box will be cloned correctly but the click event in the left box is lost (you can check it by clicking the button and see the number increase with every click)

Expected: The element should be cloned to the right box and all buttons should increase the Trigger Count by +1. Actual: The element is moving normally but the original element in the left element has lost the @click event from the button

https://github.com/MaxLeiter/sortablejs-vue3/assets/3743025/9a6aa0ea-e4cd-4293-9b14-1650c0f7ef9a

RHellenes commented 2 months ago

The problem here is that the newly cloned or moved card isn't a vue component but a rendered version of it.

I think you can solve your issue by having a state where you manage your lists (not neccessarily pinia, vuex or anything like that). When you drag the card into a new column you add it to that list. It will then look like you add 2 items every time you add an item - one is probably working as you'd expect and one is not. To solve this you can remove the node thats not a vue component. I got this technique from this https://github.com/MaxLeiter/sortablejs-vue3/issues/23#issuecomment-1238111107 thread.

Here is more or less how I solved it:

 <Sortable
      :list="list"
      item-key="id"
      :options="options"
      :data-kanban-stage-id="stage.id"
      tag="ul"
+     @add="onAdd($event)"
+     @remove="onRemove($event)"
    >
function removeNode(node: HTMLElement) {
  if (node.parentElement !== null) {
    node.parentElement.removeChild(node);
  }
}
async function onAdd(event: SortableEvent) {
  nextTick(async () => {
    const nodeItem = event.item
    const cardId = Number(nodeItem.dataset.id) // I chose to add ID to the data attribute, there is probably other ways of doing this
    const newListNodeId = Number(event.to.dataset.kanbanStageId)

   // optional: Update backend
   // Update local list  

    removeNode(nodeItem) // remove visual representation
  });
}

function onRemove(event: SortableEvent) {

  nextTick(() => {
    const cardId = Number(event.item.dataset.id)
   // optional: Update backend
   // update the local list you want it to be removed from 
  });
}

Note: My columns are in their own SFC so no need to get the parent element in the event but it would be in your case.