formkit / drag-and-drop

https://drag-and-drop.formkit.com
MIT License
1.55k stars 31 forks source link

Dnd does not work when adding new elements in list #107

Open KuchinaPS opened 1 month ago

KuchinaPS commented 1 month ago

When adding a new element to the draggable list, the new element does not appear in the list, and when refreshing the page, it is not draggable. How to correctly update the list of draggable elements when adding or removing from it?

I tried updateConfig({}), but nothing worked.

sashamilenkovic commented 1 month ago

@KuchinaPS Hey there! Are the reactive values you are updating the same values that are bound to the list of items you are iterating over? It's hard to guess without seeing your code; I'd be happy to take a look at a reproduction

KuchinaPS commented 1 month ago

This is a similar implementation. Here, after moving elements in the list, adding new ones stops working. https://codesandbox.io/p/sandbox/eloquent-bird-9x2xgl

sashamilenkovic commented 1 month ago

@KuchinaPS In your example, if I use something other than the index to set the key property, it works fine.

Here's how I updated your addNew function to use unique ids (not saying you explicitly need to use this crypto method, just an example).

const addNew = () => {
  const idx = dataList.value.length;
  const uuid = () => crypto.randomUUID();
  dataList.value.push({
    value: `${idx + 1}`,
    key: uuid(),
  });

  console.log(dataList.value);
};
KuchinaPS commented 1 month ago

In the example, everything really works fine. Maybe then the problem is in my receiving data from the backend or using the pinia storage. Because my props.data value changes, but insideValue, which I use in the list, does not.

sashamilenkovic commented 1 month ago

@KuchinaPS Yeah, shouldn't need to emit an update on the values that are "drag and dropped". My guess would be that the values you're getting from your pinia store are maybe not reactive/refs? \

BradDuns commented 1 month ago

I am seeing a similar problem. I am using realtime data from firestore that populates a pinia store. I am using dragAndDrop so I can use the pinia ref for the data. This works for regular drag and drop and I can see the updates happening in the UI and in the database.

const { userData } = storeToRefs(contactsStore)
const groupsParent = ref(null)
dragAndDrop({
  parent: groupsParent,
  values: userData.value.contactOrder,
  dragHandle: '.groupHandle',
  onDragend: (event) => {
    groupOrderChanged(event.values)
  },
  onSort: (event) => {
    userData.value.contactOrder = event.values
  },
})
function groupOrderChanged(values) {
  userData.value.contactOrder = values
  contactsStore.updateContactOrder()
}

However if the data gets changed somewhere else the UI updates but it seems like the event.values don't. For example if a value gets deleted on the server I see it removed from the list correctly but the console warns "The number of draggable items defined in the parent element..." and If I try to drag an element, the deleted item comes back when I finish dragging.

KuchinaPS commented 1 month ago

I have the same error "The number of enabled nodes does not match the number of values." when adding and removing an element.

BradDuns commented 1 month ago

I adjusted KuchinaPS's example with a pinia store and the error I am seeing: https://codesandbox.io/p/sandbox/infallible-fog-fsv9zp?workspaceId=c8c26021-4482-4a8e-87a1-ee4197aaa807

As soon as you hit add it breaks.

sashamilenkovic commented 1 month ago

@BradDuns Hey there! I took a look at your reproduction (appreciate it!). So, a couple things:

1). In your example, you are using storeToRefs to get a ref of your userData. That userData has a property, contactOrder, that is not a ref, meaning that when drag and drop updates the contact order array you provide to your dragAndDrop hook, your userData ref from your pinia store isn't going to update as well. So, you could either get contactOrders as a ref from your store, or, use a computed with a getter and setter based on the userData ref. I updated your dragAndDrop example here: https://codesandbox.io/p/sandbox/musing-wildflower-p6jw9x?workspaceId=6de9260e-0f69-4537-a09c-c9ecaac259e7.

2). useDragAndDrop hook is a composable that returns it's own reactive values, so it makes sense that your pinia store isn't getting updated.

3). And then just to put a fine point on this, in your code snippet where in your onSort callback you were assigning the values you were passing to the dragAndDrop hook to the event.values. That is unnecessary. The values you provide, when a sort occurs are being updated already by the library, there's no further work to do on that end.

BradDuns commented 1 month ago

It's interesting, for me this causes the data to get wonky and duplicate keys in my project. There must be some other interaction that causes it to lose reactivity when it round trips through Firestore. I updated the sandbox: https://codesandbox.io/p/sandbox/infallible-fog-fsv9zp?workspaceId=c8c26021-4482-4a8e-87a1-ee4197aaa807

This uses VueUse but I am using vueFire in my project with the same outcome. • I drag an item in the list and see it update in the UI and on firestore. Everything looks right. • I try to drag something else and it causes a duplicate. At that point there are objects with the same key so everything is messed up until I reset the database and reload the page. • However, if I open it in 2 browsers I can update window 1, see the change in window 2, change in window 2, see the update in 1, etc. But as soon as I try to make changes back to back in one window it breaks

sashamilenkovic commented 1 month ago

@BradDuns I'm seeing the same as you are in your reproduction there. I will take a look at it more closely when I have the chance.

BradDuns commented 1 month ago

Thanks! I’m not sure, but I wonder if it could be related to using a computed property for a nested object. I thought refs were deeply reactive, and I typically haven’t needed computed properties for this. With vue-draggable I'm passing the top level ref without issue. That said, I really like how much cleaner FormKit makes the template so trying to switch my app over.

KuchinaPS commented 1 month ago

Thanks for your help! My problem was solved by using dragAndDrop and computed for the value from the pinia store. But the values ​​started to duplicate. To avoid this, I used:

watch(list, () => {
remapNodes(question.value);
});