Stop node drop event with primeng p-tree component

Viewed 2787

I am using primeng p-tree component and i would like to forbid some node type to be drop inside another node type.

for exemple i have node with type folder and another node with type file and i only want that node of type file move inside a node of type folder. I want to forbid moving folder node inside another folder node. (and other rules

<p-tree [value]="filesTree7" draggableNodes="true" droppableNodes="true" dragdropScope="files" selectionMode="single" [(selection)]="selectedFile2" [contextMenu]="cm" (contextmenu)="onContextMenu($event)"
          (onNodeDrop)="onNodeDrop($event)"></p-tree> 

I tried to stop propagation of the drop like this :

onNodeDrop(event) {
    console.log("onNodeDrop");
    event.originalEvent.stopPropagation();
    event.originalEvent.preventDefault();
    return;
}

but it is not working.

when i have a look to primeng code here : primeng tree component code source it seems that onNodeDrop event is emit too late.

Do you have any ideas how i can implement what i need ?

3 Answers

Until this is implemented, the workaround could be to override the allowDrop method of the Tree component and write your own implementation of the drop logic. However you may (actually you should) want to use the drop logic covered in the original method and just add your bit.

Now prototyping might look like this:

  • In the component where you have used pTree, get the Tree via ViewChild

    @ViewChild(Tree) tree: Tree;
    
  • And then in the constructor, or ngOnInit or whatever suites your case, prototype the allowDrop

    Tree.prototype.allowDrop = (dragNode: any, dropNode: any, dragNodeScope: any): boolean => {
      return this._overrideAllowDrop(dragNode, dropNode, dragNodeScope)
    }
    

_overrideAllowDrop is used for brevity purpose. To cancel the drop just return false.

Moreover, you can utilize dragDropService of the tree instance to show error messages, in case drop failed.

For example:

this.tree.dragDropService.dragStop$.subscribe((data) => {
  if(this.failed){ // set failed to true in your custom drop logic
    alert('Error!')
  }
})

But this workaround has the disadvantage that the drop logic should be on the UI side and if your logic requires the backend interference, this workaround might not be of that much help.

Although the backend was involved in my case but the logic was simple so i moved it on the UI side and thus prototyping serves the purpose.

If you want that a single node isn't droppable or draggable you must set params draggable/droppable in array:

{
    "label": "Note e Commenti",
    "icon": "fa-file-word-o",
    "draggable":false,
    "droppable":false,
    "data": {
        "id": 1,
        "nome": "Note e Commenti",
        "testo": "Note e Commenti",
        "idCategoria": 2,
        "idTipologia": 1
    }
},

This has some additional details that are important, especially the bind() function

    export class myClass implements AfterViewInit {
      @ViewChild('primeTree') primeTree: Tree;  // named in HTML
    
      ngAfterViewInit() {
        // get the original function AND bind it to the tree - important!
        const originalAllowDrop = this.primeTree.allowDrop.bind(this.primeTree);
        
        // replace the allowDrop function with this
        this.primeTree.allowDrop = (dragNode: TreeNode, dropNode: TreeNode, dragNodeScope: any): boolean => {
          // let the original function do the pre-check, and then check for special cases
          return originalAllowDrop(dragNode, dropNode, dragNodeScope) && this.allowDrop(dragNode, dropNode, dragNodeScope);
        }

  }

  private allowDrop(dragNode: TreeNode, dropNode: TreeNode, dragNodeScope: any): boolean {
    // your logic here
    if (imNotHappy) {
      return false
    }
    return true;
  }
}
Related