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

Is there a way to add child node using drag and drop on existing Parent node??? #435

Open vikasdinavahi opened 6 years ago

vikasdinavahi commented 6 years ago

Here is the test case, we have a tree say it has 3 Parent nodes, Node1, Node2 and Node3. Now if Node1 & Node2 has 3 child nodes like (CN1a,CN1b,CN1c and CN2a, CN2b, CN2c) now if I want to drag CN1a and drop as a child for Node3, I cannot do it, unless Node3 has any child nodes.

So, bottom-line is we cannot create a child node for a node if there are no child nodes using drag and drop.

Your help is much appreciated, with an example or a Demo for tree.

blitzmann commented 6 years ago

I second this, I don't immediately see a way to do this with the current documentation / example. Would be nice to give a draggable item an attribute stating that other items can be dropped into it, and supplying it the property name that the item should be pushed into (like "Children")

blitzmann commented 6 years ago

Just documenting how we did it for a project at work. The problem is that angular-dnd simply relies on the position of the placeholder - it has no concept of hovering over or dropping something only another item, since it's main purpose is to reorder items. To solve this problem, we created a child drop zone for each item that, if the drop happens on that element, it does something that we define custom.

First, these two attributes should be set:

dnd-drop="callback({targetList: list, targetIndex: index, event: event})"
dnd-callback="onDrop(list, $index, targetList, targetIndex, event, item)"

dnd-drop goes on the list, and dnd-callback goes on the item.

Next, we define a child drop zone, giving it the index of the item:

        <i class="fa fa-fw fa-square-o" dnd-child-drop="{{$index}}"></i>

The index is important - while we get targetList and targetIndex from the callback, the index is the position of the placeholder, which may be above or below the item we're needing to drop. It's not reliable to use what angular-dnd gives us, se we set this attribute to the known index of the item.

$scope.onDrop = function (srcList, srcIndex, targetList, targetIndex, event, item) {
    // DO NOT USE A DEBUGGER IN THIS FUNCTION. The breakpoint will be hit, but the changes 
    // here won't be reflected in the UI, which means you will spend approx. 4 hours trying 
    // to figure out why shit doesn't work the way it's supposed to. console.log() is your friend

    //console.log('SrcList: ', srcIndex, angular.copy(srcList));
    //console.log('TargetList: ', targetIndex, angular.copy(targetList));

    if (event.target.hasAttribute('dnd-child-drop')) {
        // if we're dropping into an element with the dnd-child-drop attribute, change the target list and index to that item's children
        var targetItem = targetList[event.target.attributes['dnd-child-drop'].value]

        if (!targetItem.Children) {
            targetItem.Children = []
        }

        targetItem.expanded = true;
        targetList = targetItem.Children;
        targetIndex = targetList.length;
    }

    // do the splicing between the lists
    targetList.splice(targetIndex, 0, srcList[srcIndex]);
    if (srcList == targetList && targetIndex <= srcIndex) { srcIndex++ };
    srcList.splice(srcIndex, 1);

    //console.log('New SrcList: ', srcIndex, angular.copy(srcList));
    //console.log('New TargetList: ', targetIndex, angular.copy(targetList));
    return true; // By returning true from dnd-drop we signalize we already inserted the item.
};

The preceding function does that magic. It looks at the event.target to see what it is we dropped, and if it has the dnd-child-drop attribute, it fetches the index and pulls the item from the list. Now that we have the item that we dropped on, we simply re-assign the targetList to the .Children property of the item, and the index to .Children.length (dropping to child always puts it on the end of the array).

If it doesn't have dnd-child-drop, then it simply splices the two arrays like normal.

Hope this helps - it's probably not the correct way of doing it, and someone could probably extend the library to add an actual directive to support child dropping, but this was a quick and dirty way of getting it working. I can get a jsFiddle up and running if there's any interest.

Here's an image of what it looks like for us. Obviously you can style your drop zone any way you need :)

image

Drabynoops commented 6 years ago

If you check the CSS tab in the examples you will find this comment at the top. Along with a style with classes depending on which demo you are viewing.

/**