Shift Click Multi-Select and Copy Node with Children Implementation

8 posts, 0 answers
  1. Terry Hoiness
    Terry Hoiness avatar
    13 posts
    Member since:
    Aug 2008

    Posted 26 Oct 2010 Link to this post

    I guess these aren't really problems per se, but I'd find it odd if I were the only one that wanted to see these fixes implemented, so I have decided to create a thread with a fix in the hopes that someone will clean it up and make it a permanent solution, and that I can help others with a similar problem implement the missing functionality.

    So starting with shift click...

    <body onkeydown="setShift(event)" onkeyup="setShift(event)">
      <telerik:RadCodeBlock runat="server" ID="RadCodeBlock1">
                <script type="text/javascript">   
                   //I normally put this code right in the form (first item)
                   var shiftKey=false;
                   var treeView;
     
                    function pageLoad()
                    {
                        treeView = $find ("<%= treeYourTree.ClientID %>");
                    }
     
                   //capture the shift key for copying
                   function setShift(e)
                   {
                        if (window.event)
                        {
                            shiftKey = window.event.shiftKey;
                        }
                        else
                        {
                            if (e.shiftKey)
                            {
                                shiftKey=true;
                            }
                            else
                            {
                                shiftKey=false;
                            }
                        }
                   }
     
                   function onNodeClicked(sender, eventArgs)
                   {             
                        if (shiftKey)
                        {
                            treeView.trackChanges();
                            //We should have selected nodes if we are shift clicking
                            if (treeView.get_selectedNodes!=null)
                            {
                                //Capture the first selected node and the current selected node
                                var firstSelected = treeView.get_selectedNodes()[0];
                                var currentSelected = eventArgs.get_node();
                                //Unselect all the nodes and initialize the continue / iteration boolean
                                treeView.unselectAllNodes;
                                var cont=false;
                                 
                                //Iterate through the parent's children (iterate through the same level as selected)
                                for (var i=0; i< firstSelected.get_parent().get_nodes().get_count(); i++)
                                {
                                    currItNode = firstSelected.get_parent().get_nodes().getNode(i);
                                    cont = (cont)? !(currItNode == firstSelected || currItNode == currentSelected)
                                                   :(currItNode == firstSelected || currItNode == currentSelected);
                                     
                                   if (cont && firstSelected.get_parent().get_nodes().getNode(i))
                                            firstSelected.get_parent().get_nodes().getNode(i).select();
                                }
                                 
                            }
                            treeView.commitChanges();
                        }
                   }
         </script>
      </telerik:RadCodeBlock>
     
    <-- tree goes somewhere in here with OnClientNodeClicked="onNodeClicked" -->
    </body>
    Pay special attention to body onkey events in my provided code.  This has been tested in IE and FireFox.

    Known limitations:
      1.  When shift selecting from a child through another parent, it gets weird.  As it stands now, this allows shift selecting under the parent of the beginning of the first shift click ONLY, and I don't have the need or the time to get into fixing that, although I suspect it's an easy fix.  It does, however, allow for shift clicking up and down the current node.
      2.  You can shift click, then ctrl-click other items, but if you do a subsequent shift click, it gets weird again.  Once again, I don't have the need to fix this at the moment.

    My subsequent post will cover copying and pasting an entire tree node.  It should be noted that this script and the following script can be used in conjunction (I am actually using both on the same page).
  2. Terry Hoiness
    Terry Hoiness avatar
    13 posts
    Member since:
    Aug 2008

    Posted 26 Oct 2010 Link to this post

    Next we move on to client side copying and pasting of nodes and their children WITHOUT A POSTBACK.  Who doesn't like that?  It's lightweight and is nearly instantaneous.

    <body>
    <telerik:RadCodeBlock runat="server" ID="RadCodeBlock1">
       <script type="text/javascript">
                    var treeView;
                    var textBox;
                    var nodeHolder;
     
                    function pageLoad()
                    {
                        treeView = $find ("<%= treeYourTree.ClientID %>");
                    }
     
                    //This function is where you copy your node and hold it.
                    //...pretty straight forward
                    function copyNode(args)
                    {
                        if (treeView.get_selectedNodes!=null)
                        {
                            nodeHolder = treeView.get_selectedNodes();
                        }
                        return false;
                    }
     
                    //Pasting the node
                    //Here's where the magic happens
                    function pasteNode(args){
                        treeView.trackChanges();
                        if (treeView.get_selectedNodes != null)
                        {
                            //Get the current selected nodes
                            for (var x=0; x <treeView.get_selectedNodes().length; x++)
                            {
                                //Get the parent of the selected node
                                var parent = treeView.get_selectedNodes()[x];
                                 
                                //Iterate through the nodes previously copied
                                for (var i=0; i<nodeHolder.length; i++)
                                {
                                    if (nodeHolder[i] != null)
                                    {
                                       //And Paste the node!
                                        parent.get_nodes().add(nodeCopy(nodeHolder[i]));
                                    }
                                }
                            }
                            treeView.commitChanges();   
                        }
                         
                        return false;
                    }
     
                    //I lied... This is where the real magic happens.
                    //This is a recursive function that copies from parent to children.
                    //If you like, study this pattern, as I came up with this out of necessity (it's my pride and joy ;) ), and it comes in handy A LOT (I even adapt it for server side functions when iterating through a tree).
                    function nodeCopy(selectNode)
                    {
                        var nodeReturn = new Telerik.Web.UI.RadTreeNode();
                        nodeReturn.set_text(selectNode.get_text());
                        if (selectNode.get_nodes()!=null)
                        {
                            for (var i=0; i< selectNode.get_nodes().get_count(); i++)
                            {
                                var currentNode = selectNode.get_nodes().getNode(i);
                                nodeReturn.get_nodes().add(nodeCopy(currentNode));                           
                            }
                        }
                        return nodeReturn;
                    }
       </script>
    </telerik:RadCodeBlock>
     
            <!-- rad tree goes somewhere in here -->
            <asp:Button ID="btnCopy" OnClientClick="return copyNode()"
            Text="Copy Node(s)" runat="server" /><br />
            <asp:Button ID="btnPaste" CssClass="qsfButton" OnClientClick="return pasteNode()" Text="Paste Node(s)" runat="server" />
     
    </body>

    Features:
      1.  Will copy multiple selected rows with their children.
      2.  Will paste to multiple selected rows.

    Known Limitations:
      1.  **The COPY function does not survive a partial postback or a full postback (meaning if you paste after a copy and then a postback, it doesn't work).  I have tried to rectify this using cookies, etc, but have not had luck to date.  The latter seems obvious, but the former (partial postback) is odd to me.  I really haven't had time to work through it, but if someone sees something obvious, let me know.

    Work Around:
      1.  If you copy and paste prior to a partial post back, it will paste after the partial postback.  Once again, does anyone have any insight into this?

    For Study:
      1.  Ok, I don't like to toot my own horn, but I find my iterative self-recursive function pretty sweet.  It made my brain hurt, but once I unlocked the pattern, I started finding several uses for it when dealing with recursive trees and data.  If you are interested, I pointed it out in the code above
     

    Note: These are just thrown together, so I realize there may be some unknown deficiencies in the implementation.  Feel free to add and share whatever additional fixes you may implement, or problems you find...

    **[edit]The problem with the partial postback is only when the tree gets refreshed.  I am using this where I have a drop down that changes the entire tree.  The copy function does not survive the rewriting of the tree.  I assume this is because once the tree is rewritten, the pointer to the original node is removed with the node (the node no longer exists), but this does not explain why a copy paste prior to the reloading of the tree survives the reload[/edit]
  3. Nikolay Tsenkov
    Admin
    Nikolay Tsenkov avatar
    734 posts

    Posted 29 Oct 2010 Link to this post

    Hello Terry Hoiness,

    About the selection holding shift:
    - It's already implemented and released in the Beta of Q3 2010 RadControls for ASP.NET AJAX, that we rolled out last week. Once you get the new assemblies you need only to enable the multiselection in RadTreeView and you will be able to use it.

    About copying nodes on the client-side:
     - It's already implemented and again we released it with the latest Beta of our controls. It's called "clone" and is in the prototype of RadTreeNode client-side object representation. You also have control on whether to clone only the node or it's entire sub-tree.

    Anyway, thank you for sharing your implementations of these features in our forum, we appreciate it! Your telerik points are updated!

    Hope that you will like these and all the other new features that we included in this release!


    Regards,
    Nikolay Tsenkov
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  4. Itai
    Itai avatar
    16 posts
    Member since:
    Oct 2007

    Posted 08 Nov 2010 Link to this post

    Hi, 

    Do you have an example of using "Clone"?
  5. Nikolay Tsenkov
    Admin
    Nikolay Tsenkov avatar
    734 posts

    Posted 09 Nov 2010 Link to this post

    Hello Itai,

    Well here is an example:
    1. Markup of the TreeView:
    <telerik:RadTreeView runat="server" ID="RadTreeView1" OnClientLoad="onLoad">
    </telerik:RadTreeView>
    2. JavaScript:
    function onLoad(sender) {
     
        var tree = sender;
     
        // create root node
        var rootNode = new Telerik.Web.UI.RadTreeNode();
        rootNode.set_text("RootNode");
     
        // create rootNode's children
        for (var i = 0; i < 10; i++) {
            var node = new Telerik.Web.UI.RadTreeNode();
            node.set_text("ChildNode");
            rootNode.get_nodes().add(node);
        }
     
        // create clone of rootNode
        var rootNodeClone = rootNode.clone();
     
        var shouldCloneChildren = true;
     
        // create clones of rootNode and all of its children
        var rootNodeCloneIncludingChildren = rootNode.clone(shouldCloneChildren);
     
        // add rootNode and its two clones
        tree.get_nodes().add(rootNode);
        tree.get_nodes().add(rootNodeClone);
        tree.get_nodes().add(rootNodeCloneIncludingChildren);
    }

    Hope you will like this new feature of RadTreeView (particularly RadTreeNode)!


    Regards,
    Nikolay Tsenkov
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  6. Itai
    Itai avatar
    16 posts
    Member since:
    Oct 2007

    Posted 10 Nov 2010 Link to this post

    Hello,

    Great feature, indeed.
    But now I have other issue.
    I am cloning a node and its childes. Then, I am changing the value of the node:
    NewNode.set_value(CountID); //in the client side

    This is the error I am getting:

    Microsoft JScript runtime error: Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.
    Details: Error parsing near '

    <!DOCTYPE html P'.



    Any idea?
  7. Nikolay Tsenkov
    Admin
    Nikolay Tsenkov avatar
    734 posts

    Posted 15 Nov 2010 Link to this post

    Hello Itai,

    Well, you should probably post some code reproducing this problem because I was not able to reproduce it.

    Here is what I have tried and wasn't able to reproduce with:
    1. Markup:
    <telerik:RadTreeView runat="server" ID="RadTreeView1" OnClientLoad="onLoad">
    </telerik:RadTreeView>
    <asp:Button runat="server" ID="Button1" OnClientClick="onButtonClick(); return false;"  />
    2. JavaScript:
    function onLoad(sender) {
     
        var tree = sender;
     
        // create root node
        var rootNode = new Telerik.Web.UI.RadTreeNode();
        rootNode.set_text("RootNode");
        rootNode.set_value("RootNode");
     
        // create rootNode's children
        for (var i = 0; i < 10; i++) {
            var node = new Telerik.Web.UI.RadTreeNode();
            node.set_text("ChildNode");
            node.set_value("ChildNode");
            rootNode.get_nodes().add(node);
        }
     
        // create clone of rootNode
        var rootNodeClone = rootNode.clone();
     
        var shouldCloneChildren = true;
     
        // create clones of rootNode and all of its children
        var rootNodeCloneIncludingChildren = rootNode.clone(shouldCloneChildren);
     
        // add rootNode and its two clones
        tree.get_nodes().add(rootNode);
        tree.get_nodes().add(rootNodeClone);
        tree.get_nodes().add(rootNodeCloneIncludingChildren);
    }
     
    function onButtonClick() {
        var tree = $find('<%= RadTreeView1.ClientID %>');
     
        var nodes = tree.get_allNodes();
     
        for (var i = 0; i < nodes.length; i++) {
            nodes[i].set_value(nodes[i].get_value() + "_changed!");
        }
    }

    After the page has loaded i have executed the following javascript in the console:
    var tree = $find("RadTreeView1");
     
    var nodes = tree.get_allNodes();
     
    for (var i = 0; i < nodes.length; i++) {
        console.log(nodes[i].get_value());
    }
    and the values printed were:
    RootNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    RootNode
    RootNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    ChildNode
    Then I have pressed the button under the TreeView (to change the values of all nodes) and again executed this javascript to log the values of the nodes. The result was
    RootNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    RootNode_changed!
    RootNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!
    ChildNode_changed!

    I hope that soon we will resolve the problem!


    Regards,
    Nikolay Tsenkov
    the Telerik team
    Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items
  8. Itai
    Itai avatar
    16 posts
    Member since:
    Oct 2007

    Posted 15 Nov 2010 Link to this post

    Hi,

    I already posted the problem and the sulotion.
    the problem is in node.set_value(x)
    if x is an integer it cause a cannot convert from int to string error.
    I solve it by adding '' to the value:
    node.set_value(x+'')
Back to Top