vue.draggable.next icon indicating copy to clipboard operation
vue.draggable.next copied to clipboard

Clone - allow duplicates and how to key by index?

Open akorajac opened this issue 2 years ago • 16 comments

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.

akorajac avatar Aug 31 '21 04:08 akorajac

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>

KaliaJS avatar Sep 02 '21 22:09 KaliaJS

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

akorajac avatar Sep 03 '21 04:09 akorajac

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.)

maiksicks avatar Apr 26 '22 15:04 maiksicks

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

RustyJoeM avatar Jun 30 '22 19:06 RustyJoeM

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
    })
  }
}

anthonygore avatar Nov 01 '22 22:11 anthonygore

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.

maiksicks avatar Nov 01 '22 23:11 maiksicks

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
    })
  }
}

anthonygore avatar Nov 01 '22 23:11 anthonygore

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

MikesAtMIT avatar Nov 08 '22 16:11 MikesAtMIT

+1

boindil avatar Nov 09 '22 11:11 boindil

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:

  • Title A [click to read more]
  • Title B [click to read more]
  • Title C - This is the very interesting content of section C.

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

  • Title A [click to read more]
  • Title C - This is the very interesting content of section C.
  • Title B [click to read more]

But instead we get this:

  • Title A [click to read more]
  • Title C [click to read more] <-- is now hidden
  • Title B - This is the very interesting content of section B. <-- is expanded

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.

maiksicks avatar Nov 09 '22 14:11 maiksicks

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).

MikesAtMIT avatar Nov 09 '22 19:11 MikesAtMIT

@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.

maiksicks avatar Nov 09 '22 20:11 maiksicks

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.

MikesAtMIT avatar Nov 09 '22 21:11 MikesAtMIT

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.

vedmant avatar Dec 12 '22 22:12 vedmant

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

drahkrub avatar Jan 27 '24 12:01 drahkrub

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>

su-dan avatar Feb 28 '24 03:02 su-dan