marceljuenemann / angular-drag-and-drop-lists

Angular directives for sorting nested lists using the HTML5 Drag & Drop API
MIT License
2.16k stars 713 forks source link

Nested list cleanup in cases where multiple lists have visible placeholder elements at time of drop #450

Open johntdowney opened 6 years ago

johntdowney commented 6 years ago

Here's a fix for an annoying issue I have with this library where if I place the mouse just right I can get two placeholder elements to show up, and after I drop the object whichever list of the two competing lists did not get the event first is stuck in the dragover state until the user picks up a new object and drags over it.

I think it's handled ok by replacing stopDragover(); at line 427 with:

$rootScope.$broadcast('dnd-list-drop', event);

And then setting up a listener somewhere in the dndList directive:

scope.$on('dnd-list-drop', stopDragover);

I would have just used $emit for my nested lists that are conflicting as it didn't require the injection of $rootScope, but using $broadcast instead allows for all scope hierarchies.

blitzmann commented 6 years ago

This was driving us crazy, thanks for the simple fix. We are also getting duplicated dndDragover classes added to both the lists, would you be able to identify where a fix for that might be?

johntdowney commented 6 years ago

Change line 427 to

$rootScope.$broadcast('dnd-list-activated', {element:element});

and add back in

stopDragover();

Just below or just above it.

Add another broadcast in the dragover event, right before the placeholderNode.parentNode != listNode check:

. . . .
$rootScope.$broadcast('dnd-list-activated', {element: element});
// Make sure the placeholder is shown, which is especially important if the list is empty.
if (placeholderNode.parentNode != listNode) {
    element.append(placeholder);
}
. . . . 

Finally, change the listener I said to add in the last post so that it checks that the event is from an outside element and not from itself before stopping the dragover. This part is important:

scope.$on('dnd-list-activated', function(e, data) {
    if(element != data.element) stopDragover();
});

That'll make each active instance of the directive remove its dndDragover class and its placeholder element when another instance of the directive is triggered to add them to itself..

I don't think this is the best solution for this particular issue because it could amount to an obscene amount of broadcasts coming from the dragover event and I'm not sure of the overhead of that and the fact that it's passing an element through, but it does work I think.