Closed amitparrikar closed 7 years ago
Can confirm for angular-drag-and-drop-lists v2.1.0. Do you have figured out any work-arround/solution?
track by
doesn't work correctly because the same item exists twice in the array for some short period of time. dnd-moved
(where we're removing the item) is called after a new item was added.
I had the same problem with tracking items by unique id and got errors in the console.
Here is my workaround: instead of using dnd-moved
I moved all logic to dnd-drop
callback, doing all removings and insertions by myself in the correct order. My result code looks the following:
$scope.dndDropCallback = function(index, item) {
var i, l,
currentIndex = 0,
list = $scope.list;
for (i = 0, l = list.length; i < l; i++) {
if (list[i].id === item.id) {
currentIndex = i;
break;
}
}
// drag to the same place
if (index === currentIndex || index === (currentIndex + 1)) {
return false;
}
// remove the item from the current position
list.splice(currentIndex, 1);
// 'fix' the new position index if it was shifted after the removing
if (currentIndex < index) {
index--;
}
// insert the item into the new position
list.splice(index, 0, item);
return true;
};
Reordering in both directions works as expected now. Hope it will help you
Somehow it seems that the item passed to the dnd-moved callback when moving upwards is actually the element above the one the user is grabbing.
Any solution for this bug? I have problem with duplicates without track by!
Well if I remember correctly I skipped using "track by $index". If you have not moved much ahead with the time then consider using https://github.com/luckylooke/dragular
Maybe I've project finished. I have problem changing route, when I go back problem appears.
@fromcouch i have no idea why you disliked the suggestion, anyways that was a whole hearted suggestion.. Take it or leave it.
Did I miss the solution or why is this closed? Not be able to use 'track by' seems to be a dealbreaker for a drag and drop lib imho
Seems unsolved to me. The problem seems to stem from the directive having the wrong index / item when moving an item upwards. You can get around the problem by checking the item passed to the directive via the 'dnd-moved' function.
When an item is moved, search the list for a duplicate item. If there is no duplicate, or the index passed to the directive matches either index of the duplicates you can simply remove via list.splice(index, 1). Otherwise the item has been moved upwards and you need to remove the item via the index of the last duplicate found.
Hope that was clear enough and helps you on your way.
Regards
Had the same issue. In my case I was iterating over the array of strings. And then I was getting dupes error if I use track by $index
. Seems like iterating over the array of objects solves this issue. So instead of doing this:
ctrl.items = [ 'item1', 'item2', 'item3' ]
I did this:
ctrl.items = [ {label: 'item1', index: 0}, {label: 'item2', index: 1}, {label: 'item3', index: 2}]
and then in the view:
<li ng-repeat="item in ctrl.items">
Seems like iterating over the array of objects solves this issue.
I disagree, technical details (or bugs) may not dictate the data-structure. As long as the apps are tiny this may the a valid option. But, as I wrote before, a drag and drop lib breaking the track by
syntax is no option to use. Removing the lib seems to be the best option.
The key principle here is to not use dnd-moved
callback and use dnd-drop
callback instead, manually removing and inserting the item at new place inside one function ($ctrl.onDrop
) between dirty-checking cycles, so the ng-repeat
directive has no chance to notice the duplicate.
The template:
<ul dnd-list="$ctrl.list"
dnd-drop="$ctrl.onDrop(callback(), index)">
<li ng-repeat="item in $ctrl.list track by item.id"
dnd-draggable="item"
dnd-callback="$index">
{{item.name}}.
</li>
</ul>
The controller:
var $ctrl = this;
$ctrl.list = [{id: 1, name: 'A'}, {id: 2, name: 'B'}, {id: 3, name: 'C'}];
$ctrl.onDrop = function (srcIndex, dstIndex) {
dstIndex = dstIndex > srcIndex ? dstIndex - 1 : dstIndex;
var dropAllowed = srcIndex !== dstIndex;
if (dropAllowed) {
$ctrl.list.splice(dstIndex, 0, $ctrl.list.splice(srcIndex, 1)[0]);
return true;
}
return false;
};
this issue is still existing
I am trying to reorder my unordered list.
If I use track by $index inside my ng-repeat, then following things happen: 1) If I drag and reorder a list item from top to down it works! 2) If I drag and reorder a list item from bottom to top it creates a duplicate copy of the item being dragged.
Check the fiddle
I appreciate your help. Thanks :)