Start Building Professional
Web Apps Today


 
Categories Question details Back To List
Question  posted by Stephen on Jun 04, 2009 04:37
open dhtmlx forum
dhtmlxTree setDragHandler

I've set up setDragHandler(tondrag) - function tondrag(id,newparent, idbefore)

I can drag-n-drop at the same level to a different parent (newwparent) but only if it has a child :-
1) If it has a child then parameter newparent= the new parent id.
2) but if there is no child then parameter newparent is the "parent id" of newparent!

I therefore do not know where to insert the moved node - I need to know the real parent id as I am also doing the same on a server ie reflecting the tree on the server - directory tree. How can I obtain the true parent id?

Also idbefore (see above) is in fact the id after the insert point and not as comment says in source: @eventparam: if node droped as sibling then contain id of item before whitch source node will be inserted.

1) same parent - mv P1-2 above P1-1
P1
-1
-2
can become
P1
-2
-1

2) different parent with child - move P2-2 below P1-2
P1
-1
P2
-2
-3
can become
P1
-1
-2
P2
-3

3) different parent no child - move P2-1 to P1 fails as the parent parameter contains R1 and not P1
R1
-P1
-P2
--1
--2

I think I can solve this uising
if new parent parameter = parent of target parent
var nextindex= getIndexById(idbefore);
var realparent= getItemIdByIndex(newparent,nextindex-1);
but I also need to check for null ie if target parent is last then next parent will not exist ie null

Answer posted by Support on Jun 04, 2009 05:19
Which drag-behavior you are using?

If you need to drop item only as child of something - you can use 

tree.setDragBehavior("child")

Now the onDrag event will provide only ID of item, on which source item was dropped, and you need not include any other parameters in calculations. 

tree.setDragBehavior("child")
tree.attachEvent("onDrag",function(sid,tid){
       if (this._allow_next_call) return true; //allow inner calls
       this._allow_next_call = true;

       if (this.getSubItems(tid)) //item has child
             this.moveItem(sid,"child",tid);
      else 
             this.moveItem(sid,"item_sibling",tid); //if there is no child - drop on the same level

       this._allow_next_call = false;
       return false; //block default drag processing
})




Answer posted by Stephen on Jun 04, 2009 09:45
I'm using tree.setDragBehavior("sibling")

Examples to illustrate required use

1) reorder node items that are at the same level eg authors
eg move StephenKing above JaneAusten
Its useful to see the horizonal line that appears when moving -
I dont see this using tree.setDragBehavior("child") as I assume
you are expected to drop eg StephenKing into Horror parent folder icon

2) move a node item to another parent
eg move JaneAusten from parent Fiction to parent Classics

Also then want to do copy instead of move

Answer posted by Alex (support) on Jun 05, 2009 00:33
Hello,
there is also complex mode tree.setDragBehavior("complex") It allows  to  combine  both  "sibling"  and "child"  modes.
To enable copying:
 tree.enableMercyDrag(true)

Answer posted by Stephen on Jun 05, 2009 02:35
I've tried both tree.setDragBehavior("complex")
and they do not work for the specific problem I've already described,
take the following:-

--books
----classics
----horror
-------frankenstein

I want to move "frankenstein" as follows

--books
----classics
-------frankenstein
----horror

but this is not possible as my event handler function tondrag(id, newparent, idbefore)
does not give the target parent, it only supplies the target parents parent as follows

id=/books/horror/frankenstein
newparent=/books <<<<<<<<< I need /books/classics
idbefore=/books/horror/ <<<<<<this is the 'parent' after the insertion point not before

but if I have start with

--books
----classics
-------dracula
----horror
-------frankenstein

I can move as follows

--books
----classics
-------dracula
-------frankenstein
----horror

because my hander function tondrag(id, newparent, idbefore) supplies the target parent

id=/books/classics/frankenstein
newparent=/books/horror
idbefore=/books/classics/dracula

Remember I'm duplicating the tree layout and item order on my server.
So I do the move both in the tree and on the server ie once the drag-n-drop
takes place a reload from the server will leave the tree as is following
the drag-n-drop.
Answer posted by Stephen on Jun 05, 2009 02:37
Sorry above should have read as below ie frankenstein moved to above dracula

"but if I have start with

--books
----classics
-------dracula
----horror
-------frankenstein

I can move as follows

--books
----classics
-------frankenstein
-------dracula
----horror
Answer posted by Stephen on Jun 05, 2009 03:04
Ok I see whats wrong, the reason I see a failure is that dhtmlxTree sees
my horizontal drag line as applying to parents ie
1. if books exist then the line applies to books
2. if no books exist in a category like classics then the line applies to categories and not books

For 2 to get the same behaviour as 1 you need to drag the cursor to the category icon and then drop :(

Its annoying as I only want to allow drag-n-drop at the same level as per my examples.

Take what works - we have
--classics
<-----------------drag bar-------------->
----dracula
--horror
----frankenstein  <<< selected

then visually drag-n-drop is seen with a horizontal drag bar ie
release key and frankenstein  is dropped above dracula as a book

--classics
<-----------------drag bar-------------->
--horror
----frankenstein  <<< selected

again visually drag-n-drop is seen with a horizontal drag bar but
release the key and frankenstein  is dropped above horror but
not as a book instead as a new category

--classics
--frankenstein
--horror




Answer posted by Stephen on Jun 05, 2009 03:28
Question is then how do I solve this?

I need to use the horizontal bar for ordering nodes.

[A] Tell the user to only use the horizontal bar when another book exists and
if not use the category icon? Do this via a popup? Not good.
A user would expect the same behaviour.

[B] still use the bar when the category is empty ie and in my code find the new
parent (id) into which I am dropping my book/

As stated my event handler function tondrag(id, newparent, idbefore)
does not give the target parent, it only supplies the target parents parent as follows

id=/books/horror/frankenstein
newparent=/books <<<<<<<<< I need /books/classics
idbefore=/books/horror/ <<<<<<this is the 'parent' after the insertion point not before

whereas I need
newparent=/books/classics

and as I stated in the first posting: maybe I can use
idbefore=/books/horror/ <<<<<<this is the 'parent' after the insertion point

var nextindex= getIndexById(idbefore);
var realparent= getItemIdByIndex(newparent,nextindex-1);

but if target parent is last then next parent will not exist ie
idbefore=null

in which case maybe I get all the subitems for /books and the last subitem is the new parent.



Answer posted by Support on Jun 05, 2009 05:13
Working sample was sent by email. ( the info received in onDrag event is really not suitable for desired use-case - but it can be transformed to necessary one ) 


tree.attachEvent("onDrag",function(sid,tid,bid){
   if (this._allow_next_call) return true; //allow inner calls
   this._allow_next_call = true;

//get previous item ID
   if (!bid){
      if (tree.hasChildren(tid)) //last child
         bid = tree.getItemIdByIndex(tid,tree.hasChildren(tid)-1)
      else
         bid = tid; //has not childs - drop on parent
   } else {
      var ind = tree.getIndexById(bid);
      if (ind) //if has previous item
         bid = tree.getItemIdByIndex(tid,ind-1);
      else
         bid = tid; //else drop on parent
}

if (bid && tree.hasChildren(bid))
   tree.moveItem(sid,"item_child",bid); //drop if item already has childs
else
   tree.moveItem(sid,"item_sibling_next",bid);

this._allow_next_call = false;
return false; //block default drag processing
})

Answer posted by Support on Jun 05, 2009 05:15
>>it only supplies the target parents parent as follows
targetID - ID of parent to the item, which is below drag-line-marker ( parent of below ) 
beforeID - ID of item, which is below drag-line-marker 

if drag item placed after last child on level - targetID will point to the parent of above item, and beforeID will have null value. 
Answer posted by Stephen on Jun 05, 2009 09:28
Thanks for the quick response.

I pasted the handler into my code, renaming "tree" to my tree but
its not working as expected eg if I drag-n-drop dracula to under classics

-books
--classics
--horror
----dracula

results in

--classics
--dracula
--horror

(and same for moving from classics to horror)

Problem is classics has no children and move 'sibling' is used
if (bid && tree.hasChildren(bid))
  tree.moveItem(sid,"item_child",bid); //drop if item already has childs
else
   tree.moveItem(sid,"item_sibling_next",bid);

If  I amend and use tree.moveItem(sid,"item_child",bid);
then it works except in the drop handler where I call
s=atree.getSubItems(tid)
Although the handler parameter tid is correct == books/classics
the call returns with s= "books/horror/dracula" and so a count of 1 not 0.


Answer posted by Stephen on Jun 05, 2009 12:26
I think my real problem lies with onDrop
"Event raised after drag-and-drop processed. Event also raised while programmatic moving nodes."
tree.getSubItems(tid) is coming back with incorrect data

If dracula has moved from books/horror to books/classics
then ondrop has tid=books/classics and I'd expected
tree.getSubItems(tid)  to return with books/classics/dracula
but instead tree.getSubItems(tid) returns with books/horror/dracula
which is wrong: onDrop is meant to be after the move but I assume
books/horror/dracula is a symbolic link ie the move is incomplete at this point
Answer posted by Stephen on Jun 06, 2009 10:45
On second thoughts the problem lies with me and thinking in terms of a mv dir
ie I thought because dracula moved from horror/ to classics/ in the tree then
the itemId will change to reflect that but why should it. Its up to me to
rename it : so tree.getSubItems(tid) is ok.


Answer posted on Jun 08, 2009 05:50
>>I thought because dracula moved from horror/ to classics/ in the tree then
>>the itemId will change to reflect that but why should it.
Yep, the IDs are preserved as is. 
Technically they can be changed by using tree.changeItemId - it can be used after moveItem to set new ID, according to the scheme, used in your case

>>I think my real problem lies with onDrop
If you are using onDrag with custom logic - it better place your custom logic after calling moveItem with custom parameters , because here all necessary ids already known