Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Managing state when moving items between lists #23

Closed
nlhkabu opened this issue Aug 22, 2022 · 16 comments · Fixed by #97
Closed

Managing state when moving items between lists #23

nlhkabu opened this issue Aug 22, 2022 · 16 comments · Fixed by #97

Comments

@nlhkabu
Copy link
Contributor

nlhkabu commented Aug 22, 2022

Thanks again for this lib.

I have several lists on my page, and want to be able to drag items between lists. For this, I give every list the same sortable group option.

This is how I am trying to manage the state when dragging an item between lists:

  1. use remove event to remove item from the state
  2. use add event to add the item back to the state in its new location

My state is updating just fine, however, the orginal DOM element is moved to the new list (and not removed from the DOM). This results in duplicates (because my state also creates a new element).

Currently, it looks like my options are to either:

  1. manually remove the DOM element with event.item.remove() OR
  2. force re-render my component to keep the state in sync

Both of these seem hacky - is there a better approach?

@nlhkabu
Copy link
Contributor Author

nlhkabu commented Aug 22, 2022

For context, here is the problem in action:
https://github.com/nlhkabu/sortablejs-vue3/tree/add-store

@MaxLeiter
Copy link
Owner

Thanks for the detailed issue + repro! I'll try and investigate tonight or tomorrow and will respond back here

@Chuuone
Copy link

Chuuone commented Sep 6, 2022

If you compare the approach of another library they also seem to remove the item onDragAdd manually, allowing for the component to render its own with v-for. I dont think sortableJS provides any options for renderless listing.

https://github.com/SortableJS/Vue.Draggable/blob/017ab498428efef966d72ea2be547cb0213cd6bb/src/vuedraggable.js#L406

@MaxLeiter
Copy link
Owner

MaxLeiter commented Sep 6, 2022

Thanks for the info, @Chuuone. I got a little caught up with work and haven't worked on this yet, but that's very helpful.

@nlhkabu
Copy link
Contributor Author

nlhkabu commented Sep 6, 2022

Thank you @Chuuone - that's what we decided to do in the end :)

@goldyfruit
Copy link

goldyfruit commented Oct 21, 2022

I'm facing kind the same behavior, with vue.draggable.next I can move an item from a list to an other. I don't really know how to workaround this.

<draggable
     :component-data="{name:'fade', type: 'transtion-group'}"
     v-model="modules" group="modules"
     item-key="module"
     class="q-pa-md row items-start q-gutter-md"
     :move="test"
     @start="dragging = true"
     @end="dragging = false"
     v-bind="dragOptions">
<Sortable
    :list="modules"
    item-key="module"
    :options="options"
    class="q-pa-md row items-start q-gutter-md"
    group="modules">

@MaxLeiter
Copy link
Owner

@goldyfruit are your groups the same? Or am I misunderstanding what you want to do?

@sjkey
Copy link

sjkey commented Dec 27, 2022

I am also having the same issue. In my case, when I move an item between lists I am removing and adding it in the database, which then reflects the state of the lists. As above my state reflect it correctly, hoverer Sortable duplicates the item in list.

Firstly it creates some ghost item with draggable="false" and then my proper item is added which makes two same items at the end.

@MaxLeiter
Copy link
Owner

@jakubserwin any API suggestions on how you would like to interact with the lib would be welcome. I use it in a stateless context so I don't need to keep it in-sync besides on load.

@nlhkabu
Copy link
Contributor Author

nlhkabu commented Dec 31, 2022

@MaxLeiter - I wonder if supporting this fork of sortable would enable folks to manage state better?
SortableJS/Sortable#2195

I have been looking into this as I am experiencing another issue where the DOM and state get out of sync (when adding an item to the list after drag and drop it is added in the wrong place).

I tried to create a fork of your library but got stuck. I would be happy to work through this with you if you think it is a viable option for the lib.

@Gahotx
Copy link

Gahotx commented Apr 5, 2023

I have the same problem. When I use the method to manage the data in pinia, it will affect the sorting of DOM. It is a similar problem to this issue vueuse/vueuse#2924

@kburton-dev
Copy link

Anyone got a solution to this yet?

@MaxLeiter
Copy link
Owner

Sorry @nlhkabu, I completely missed your offer. It's probably too late to be of help for you, but that fork seems like a great solution with the caveat of multidrag not working. I intend to look into it soon

@yvesh
Copy link

yvesh commented Jun 5, 2023

I'm personally just including the group (not the sortable group, but a key of mine, mostly group / index) in the remove and add methods, don't use the other ones.

<Sortable :list="itemsA"
                  group="same"
                  @add="(event) => handleAdd(event, 'a')"
                  @remove="(event) => handleRemove(event, 'a')"
                  @end="(event) => handleEnd(event, 'a')"
>
...
</Sortable>

<Sortable :list="itemsB"
                  group="same"
                  @add="(event) => handleAdd(event, 'b')"
                  @remove="(event) => handleRemove(event, 'b')"
                  @end="(event) => handleEnd(event, 'b')"
>
...
</Sortable>

Now when you move an item from list a to b -> handleAdd is called with "b" and handleRemove is called with "a".. Alternatively you can get the html item from event.item too (Check SortableEvent).. (Add a data attribute or something to match it easily) and update the store accordingly.. Not sure if there is an easier way @MaxLeiter?

P.S.: handleEnd is for sorting the list in this sample.

@garbit
Copy link

garbit commented May 7, 2024

I'm experiencing the same issue where I have two separate Sortable lists that I want to move items between using a shared group name. I load the state of the sortable lists from a pinia store and I detect that items have been moved using the @end event.

When I move an item between the two lists the item is removed from the previous parent but is then duplicated in the new parent list.

I managed to get around this by listening to the @end event, performing my pinia update, and then calling event.item.remove() which removes the shadow element in the new parent group.

async function moveItem(event: { newIndex: number | undefined, oldIndex: number | undefined, item: HTMLElement, to: HTMLElement, parentId: string }) {
  const op = {
    type: EditorOperationType.MOVE,
    fromIndex: event.oldIndex,
    toIndex: event.newIndex,
    fromParentId: parentItem.item.itemId,
    toParentId: destinationParentItem.item.itemId,
    item: draftItem.item,
    itemId: draftItem.item.itemId,
  } as MoveOperation
  myStore.addOperation(op)
  event.item.remove() <- here
}

<template>
   <Sortable
    :list="myStore.state.list"
    item-key="id"
    :options="{
      handle: '.grab-handle',
      group: {
        name: 'items',
      },
    }"
    class="flex flex-col gap-2"
    :data-id="itemId"
    @end="(event) => moveItem(event)"
  >
    <template #item="{ element: item }">
      <div :id="item.id class="flex items-center" :data-id="item.id>
        <div :key="item.id" class="draggable grab-handle">
          <div>
            <Icon name="mdi:drag" class="cursor-move size-5 text-gray-500" />
          </div>
        </div>
        <div class="flex-1">
             {{ item.id }}
        </div>
      </div>
    </template>
  </Sortable>
</template>

update
Using event.item.remove() now seems to be causing issues when the underlying state changes from the pinia store and I can no longer retrieve the id of the removed object.

This is still an issue it seems.

update 2
Based on comments here: #57 (comment) it seems that using a dynamic :key on the Sortable list resolves this issue.

@garbit
Copy link

garbit commented Jun 14, 2024

Further update to this - when using uuidv4() as the key the list is forced to re-render however this is causing any input fields within the nested list to lose focus when any field in the list is clicked on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants