Login Register

Tree widget: when a parent node becomes a child node of its son

Using Dnd feature in tree, you can experience what happens if you drag a parent node and drop it on any of its child nodes:
the parent node and the child node suddenly disappears from the view!

here is what happens in dijit/tests/tree/test_Tree_DnD.html:
treePic.jpg

The explanation simply is we just created an isolated subtree:
"Fruits" node was referencing "Citrus" node, but when "Fruits" was dropped to "Citrus", there was no more node referencing "Citrus". So tree updated itself showing neither "Fruits" neither "Citrus", because there wasn't a path to "Citrus" and "Fruits".

I think allowing or disallowing users to drop a node to one of its child could be a feature in Tree Widget.

My actual solution to avoid users dropping a node to its child is (this code works with multiple nodes drag and drop, and with any relationship degree):

<script type="text/javascript">
 
  var actualDraggedNodes = [];

  /*
   *
   */

  function checkPaternity(source, draggedNodes, newParentNode)
  {
      // parents is newParentNode to Root path described as a list of nodes
      var parents = [];
      parents.push(newParentNode);

      var parentNode = newParentNode;
      while (parentNode != source.tree.rootNode)
      {
        parentNode = parentNode.getParent();
        parents.push(parentNode);
      }

      // for each parent check if at least one is dropped to its child
      for(var i=0; i<parents.length; i++)
      {
        var position = draggedNodes.indexOf(parents[i]);
        if (position >= 0)
        {
          return false;
        }
      }

      return true;
  };

  /*
   *
   */

  function dndAccept(source,nodes)
  {
      // save dragged nodes to global var
      actualDraggedNodes = nodes;

      if (source.tree.id=="folderT"){
        return true;
      }
      return false;
  };

  /*
   *
   */

  function itemTreeCheckItemAcceptance(node,source)
  {
      var draggedNodes = [];
     
      // get nodes from global var array
      for(var i=0; i<actualDraggedNodes.length; i++)
      {
        var draggedNode = dijit.getEnclosingWidget(actualDraggedNodes[i]);
        draggedNodes.push(draggedNode);
      }

      return checkPaternity(source, draggedNodes, dijit.getEnclosingWidget(node));
  };

</script>

...

<div dojoType="dijit.Tree" id="myTree" jsId="myTree"
            model="myTreeModel"
            dndController="dijit._tree.dndSource" checkAcceptance="dndAccept"
            checkItemAcceptance="itemTreeCheckItemAcceptance"
            onDndDrop="dndDrop"
            root="{ name: 'World' }">

Hope it can help! :)

looks good

Might be able to consider this a manifestation of the bug that Tree doesn't support items w/multiple parents... not sure. Although supporting cycles is weirder than supporting a DAG. Probably best to just prohibit it like you did.

BTW instead of

var draggedNodes = [];
     
      // get nodes from global var array
      for(var i=0; i<actualDraggedNodes.length; i++)
      {
        var draggedNode = dijit.getEnclosingWidget(actualDraggedNodes[i]);
        draggedNodes.push(draggedNode);
      }

You should be able to do something like

var draggedNodes = dijit.map(actualDraggedNodes, "return dijit.getEnclosingWidget(item);");

Also, concerning:

// for each parent check if at least one is dropped to its child
      for(var i=0; i<parents.length; i++)
      {
        var position = draggedNodes.indexOf(parents[i]);
        if (position >= 0)
        {
          return false;
        }
      }

      return true;

Probably you didn't try this on IE, the myArray.indexOf() doesn't work there. That's why dojo has an indexOf() function. You could use it and reduce the code w/something like:

return dojo.every(parents, "return draggedNodes.indexOf(item) == -1");

It might also make sense to be using hashes rather than arrays... if you have a structure like

var myHash = { foo: true, bar: true, zaz: true }

Then to check if myHash contains zaz you just do if(myHash["zaz"]);

=========
Bill Keese
Project Lead (aka BDFL) of Dijit