SortableJS / vue.draggable.next

Vue 3 compatible drag-and-drop component based on Sortable.js
https://sortablejs.github.io/vue.draggable.next/#/simple
MIT License
3.9k stars 531 forks source link

Clone - allow duplicates and how to key by index? #82

Open akorajac opened 3 years ago

akorajac commented 3 years ago

https://github.com/SortableJS/vue.draggable.next/blob/master/example/components/clone.vue

How could I pass the index in the itemKey prop instead of a value from the object. In my case in want to allow duplicates inside the array that the items are being dropped to.

KaliaJS commented 3 years ago

I'm also interested by using index has key. Normally I use this way in a v-for :


<div
  v-for="(element, key) in elements"
  :key="`element${key}`"
>
  {{ element }}
</div>
akorajac commented 3 years ago

it doesn't seem possible the default should be set to index automatically tbh..

maiksicks commented 2 years ago

I did it this way:

<draggable
    v-model="myArray"
    :item-key="((item) => myArray.indexOf(item))"
    ...
    >

   <!-- ... -->
</draggable>

Not beautiful but works fine.

EDIT: Will only work if your array elements are unique. (Two objects with the same content are also unique when they are two different instances.)

RustyJoeM commented 2 years ago

i've run into same issue... am a bit puzzled by the most simple case of plain array with no props not being covered :(

anthonygore commented 2 years ago

Another solution is to wrap the array in a computed property and add an id

computed: {
  myArrayWithId() {
    return this.myArray.map((item, index) => {
      item.id = index
      return item
    })
  }
}
maiksicks commented 2 years ago

Another solution is to wrap the array in a computed property and add an id

The problem is that the id changes when dragging the elements around. It may be better to clone the array and assign the IDs only once. Then watch the cloned array for changes (adding or removing elements) and write those changes without the ID back into the original array.

anthonygore commented 2 years ago

Good point. Maybe the ID could be based on some intrinsic property rather than the array index, for example, a JSON string of the object?

computed: {
  myArrayWithId() {
    return this.myArray.map(item => {
      item.id = JSON.stringify(item)
      return item
    })
  }
}
MikesAtMIT commented 2 years ago

Chiming in here that I would also like the ability to do this without having the clone the array

boindil commented 2 years ago

+1

maiksicks commented 2 years ago

without having the clone the array

Short answer: not possible

Long answer: Why does sortable need unique values?

Imagine you have a sortable list with a title and a description. The description can be toggled. Looks like this:

When we swap B and C, we would expect to get this result:

But instead we get this:

This happens because we are using the index as the key element. Vue.js only knows that the we toggled the third element of our array but as we swapped B and C, B is now our third element. Therefore we need to have unique elements inside our array.

MikesAtMIT commented 2 years ago

Doesn't this just depend on the data structure of the elements and what is keeping track of the visibility? If your data were:

const list = [
  { title: 'A', description: 'descA', visible: false },
  { title: 'B', description: 'descB', visible: false },
  { title: 'C', description: 'descC', visible: true },
]

Then re-ordering the elements would preserve the visibility. If you were keeping track of the visibility states in a second list, then sure, re-ordering would screw that up. Though it seems like it would be nicer in the template to use the object properties rather than lookup in a list:

<div v-if="element.visible"></div>
vs
<div v-if="index in visibleIndices"></div>
or if you were tracking IDs
<div v-if="element.id in visibleIds"></div>

It just seems like KaliaJS's v-for usage of key is pretty common pattern that would be appropriate here (and works in the Vue 2 version of this library).

maiksicks commented 2 years ago

@MikesAtMIT When you store all data inside the array and don't rely on the internal component variables, it doesn't matter what type of key you use.


It should even work without the visible property by using the object index as the key and an internal variable of the foldable component (:item-key="((item) => list.indexOf(item))"). The objects in your example list are all unique (no reference).

I was facing the issue where the internal component variables haven't swapped with the elements inside the array and could solve it by using the item key above without the need of adding an id property.

MikesAtMIT commented 2 years ago

Yeah, the function for the key should work in my actual use case, so thanks for the suggestion. Still, seems like a weird workaround for essentially just using the index as the key like you could in a v-for.

vedmant commented 1 year ago

What I terrible idea to iterate own way instead of letting user write his own code. Why just not to keep this how it was done in Vue 3, looks like not only Vue 3 designed to break everything, now every 3rd party lib is also breaking everything and making stupid changes.

drahkrub commented 9 months ago

see my comment in https://github.com/SortableJS/vue.draggable.next/issues/197#issuecomment-1913134306

su-dan commented 8 months ago

when you drag an item, you can add a unique id into the clone item like this, then you can use the id as item-key:

// data source
 <draggable :clone="onClone">
      ...
</draggable>

const onClone = (item) => {
    return {
      ...item,
      id: uuidv4(),
    };
 };

// put area
<draggable item-key="id">
      ...
</draggable>