Implementing Drag and Drop in an Angular Material tree - nest node when another node is hovered
Asked Answered
W

0

9

This question has been brought up a number of times on SO, but not recently, and the example here is more complete than other ones I have seen. The best implemention of drag and drop with a material tree is this one posted here on medium by @inorganik.

I have cloned the StackBlitz here.

However, my issue is that you can only move nodes if you have at least one child element on a node. For example, if you remove all the child nodes from Application and then try to add it back to Application.

The dropped node will only be inserted into the parent if the node has siblings, at line 232:

    // insert node 
    const newSiblings = findNodeSiblings(changedData, nodeAtDest.id);
    if (!newSiblings) return;
    newSiblings.splice(relativeIndex, 0, nodeToInsert);

It seems logical to me that the solution would be to track if the user was hovering the moved node over another node when they dropped it, and then assume that the user wants to make the hovered node the parent, please see the screen grab:

angular material drag and drop tree

My question is: is this the best way to go about solving this problem? If so, how do track the hovered node using the Angular Drag Drop CDK?

UPDATE:

I have gotten this functionality partially working. I've updated the StackBlitz here.

I updated the dragHover method based on other implementations of mat-tree DnD I've found which sets insert mode based on how far to the right the user drags the hovered node:

  dragHover(node: FileFlatNode) {
    // untype the event
    const newEvent: any = event;
    const percentageX = newEvent.offsetX / newEvent.target.clientWidth;
    if (percentageX > .25) {
      this.dragNodeInsertToParent = true;
    } else {
      this.dragNodeInsertToParent = false;
    }

    if (this.dragging) {
      clearTimeout(this.expandTimeout);
      this.expandTimeout = setTimeout(() => {
        this.treeControl.expand(node);
      }, this.expandDelay);
    }
  }

I added this to the drop method. If dragNodeInsertToParent is true, find the index of the node at destination of the drop, and push the node to insert into its children array.

    if (this.dragNodeInsertToParent) {
      const indexOfParent = newSiblings.findIndex( element => element.id === nodeAtDest.id);
      newSiblings[indexOfParent].children.push(nodeToInsert);
    } else {
      newSiblings.splice(relativeIndex, 0, nodeToInsert);
    }

Now, if you pull the node out far enough to the right, insert mode is triggered, and you can add the child back to an empty parent node:

animated gif of node being removed from and added to parent

Anyway, still working on this, if anyone has any ideas for making this work more intuitively, please leave a comment.

Withdrawal answered 2/5, 2020 at 20:5 Comment(5)
Hello, I don't have an answer but I ran into the exact same problem. Did you happen to find any solution?Solid
My company abandoned this approach because it's only useful for moving very short lists of files. Once there are hundreds of files and folders, it becomes far too cumbersome to drag and drop them within a single browser window. We are using the google drive approach to show only flat lists of files at any given time.Withdrawal
@Withdrawal so how did you implement drag and drop for flat lists?Babylon
@OldGaurd01, Not sure if this is what you mean, but flat lists are covered in the docs: material.angular.io/cdk/drag-drop/overviewWithdrawal
I added dummy node as a leaf node, which would not have expand icon and css would have more more blurred our node html representation with Text "Drag Here", so user would know it's just place holder so we have designated drop area for leaf node's child. In Rebuild Tree function I have called two function, first will clear out all dummy nodes, and second one will add dummy nodes from scratch so we always have synced tree. Its dirty approach and would recommend only incase we have relatively smaller tree with few hundred nodes other wise it's nightmare for UI.Katharinekatharsis

© 2022 - 2024 — McMap. All rights reserved.