Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    TreeGrid reorder issue

    I'm having a problem with my TreeGrid implementation.

    I have a tree grid that is tied to a RestDataSource. The DataSource has a parentID foreign key relationship to create the tree.

    When I drag and drop to reorder a folder inside the same parent folder I can get the information to properly send to the server, and I can successfully update the folder order on the server side. What I can't seem to do is get the client to update it's view to match the server.

    So I have:
    Code:
    Main Folder
    ---->1st sub folder
    ---->2nd sub folder
    ---->3rd sub folder
    ---->4th sub folder
    If I try and move the 4th sub folder between the 1st and 2nd sub folder (or anywhere else inside the Main Folder) the DataSource automatically fires off an updateData request that I catch on the server and process.

    I have tried sending back a simple success status, and I've tried sending back the whole (modified) Main Folder structure. Neither of which seems to cause the client to update properly. Of course if I reload the page it comes back exactly as it should.

    I've even tried creating an 'order' field in the DataSource (and TreeGrid) and setting sortField: 'order' but it doesn't seem to make a difference. I've even called sort('order') inside dataArrived() to no avail.

    If I display the 'order' field in the tree I can see that the entries are properly updated from the server (when I send back the whole 'Main Folder' structure), but the folder list is never re-sorted by the updated values.

    So, a long story short:
    What exactly do I need to return from the server in order to get the client to properly update it's view?

    Thanks,
    -Chris

    #2
    See the FAQ about grids not updating to see what to do next.

    Comment


      #3
      After searching all over this website looking for a FAQ section I finally figured out it was in the SmartGWT Technical Q&A section. Silly me for not looking there since I'm not using SmartGWT.

      I did search the forums trying to find an answer to my issue, and read every single post that seemed related that I could find, and tried every suggestion that I could find in those posts as detailed in my first post.

      I have now read the FAQ section found here:
      http://forums.smartclient.com/showthread.php?t=8159&highlight=FAQ#aGrid

      I have done numbers 1 - 3 listed in the FAQ's (they were done properly to begin with, at least as far as I can tell).
      1. verify your DataSource has a primaryKey field declared. This is how the different
      components understand that the record that has been updated is the same as the one they have
      cached.

      2. make sure the data you are returning on successful "add" and "update" operations is
      correct. The DSResponse object is expected to contain the data-as-saved. The primary
      key value returned on an update must exactly match the value in the cached record.

      3. enable the "ResultSet" (for ListGrids, ComboBoxItem or similar components) or "ResultTree"
      (for TreeGrids, PickTreeItems or similar components) to see what's going on - this log will
      show you the data being used to update caches and why it was either applied or ignored.
      The result of #3 is:
      Code:
      [
      {id: 4,
      parentID: 1,
      title: "1st sub folder",
      order: 2,
      isFolder: true},
      {id: 5,
      parentID: 1,
      title: "4th sub folder",
      order: 4,
      isFolder: true},
      {id: 3,
      parentID: 1,
      title: "2nd sub folder",
      order: 6,
      isFolder: true},
      {id: 2,
      parentID: 1,
      title: "3rd sub folder",
      order: 8,
      isFolder: true}
      ]
      03:27:13.318:XRP5:DEBUG:ResultTree:isc_ResultTree_64 (created by: isc_TreeGrid_54):updated cache, 4 out of 4 rows remain in cache, 0 row(s) added.
      Note: The 'order' field is based on a nested set representation of a tree so the numbers are intentionally not sequential, but they are in the correct ascending order.

      So, as far as I can tell I am doing everything correct, but the display order still isn't updating.

      As a recap, the 'order' field that I currently have displayed in the tree for debugging purposes does in fact get updated like it should (indicating that the cache was properly updated), but the tree remains incorrectly sorted.

      So, what am I doing wrong?

      Comment


        #4
        So, after 2 days of fighting with this I still haven't been able to get it to work so I figured I better post some example code that demonstrates the problem. These two examples are stripped down versions of the Tree Drag Re-Parent code found here:
        http://www.smartclient.com/index.jsp#treeDragReparent

        The difference is that instead of using an array I'm using a DataSource (which I assume is the more real life scenario).

        The first example is what I would ideally like. The second is closer to the original treeDragReparent example (since it creates an explicit isc.Tree)

        Preferred Method
        Code:
        isc.TreeGrid.create({
            ID: "employeeTree",
            width: 300,
            height: 200,
            canReorderRecords: true,
            canAcceptDroppedRecords: true,
            nodeIcon:"icons/16/person.png",
            folderIcon:"icons/16/person.png",
            showOpenIcons:false,
            dropIconSuffix:"into",
            closedIconSuffix:"",
            fields: [{ name: "Name" }],
            autoFetchData: true,
            dataSource: isc.DataSource.create({
              ID:"myDS",
              clientOnly: true,
              dataFormat:"json",
              rootValue:'root',
              fields:{
                id:{
                  title:"Employee ID",
                  primaryKey:true,
                  name:"id",
                  type:"integer",
                  required:true
                },
                parentID:{
                  hidden:true,
                  name:"parentID",
                  required:true,
                  rootValue:'root',
                  foreignKey:"myDS.id"
                },
                Name: {
                  title:"Name",
                  name:"Name"
                }
              },
              testData:  [
                { id:4, parentID:'root', Name:"Charles Madigen" },
                { id:189, parentID:4, Name:"Gene Porter" },
                { id:265, parentID:4, Name:"Olivier Doucet" },
                { id:264, parentID:4, Name:"Cheryl Pearson" }
              ]
            })
        });
        Would settle for this, but it doesn't work either.
        Code:
        isc.TreeGrid.create({
            ID: "employeeTree2",
            width: 300,
            height: 200,
            canReorderRecords: true,
            canAcceptDroppedRecords: true,
            nodeIcon:"icons/16/person.png",
            folderIcon:"icons/16/person.png",
            showOpenIcons:false,
            dropIconSuffix:"into",
            closedIconSuffix:"",
            fields: [{ name: "Name" }],
            autoFetchData: true,
            data: isc.Tree.create({
                modelType: "parent",
                nameProperty: "Name",
                idField: "id",
                parentIdField: "parentID",
                dataSource: isc.DataSource.create({
                  ID:"myDS2",
                  clientOnly: true,
                  dataFormat:"json",
                  rootValue:'root',
                  fields:{
                    id:{
                      title:"Employee ID",
                      primaryKey:true,
                      name:"id",
                      type:"integer",
                      required:true
                    },
                    parentID:{
                      hidden:true,
                      name:"parentID",
                      required:true,
                      rootValue:'root',
                      foreignKey:"myDS2.id"
                    },
                    Name: {
                      title:"Name",
                      name:"Name"
                    }
                  },
                  testData:  [
                    { id:4, parentID:'root', Name:"Charles Madigen" },
                    { id:189, parentID:4, Name:"Gene Porter" },
                    { id:265, parentID:4, Name:"Olivier Doucet" },
                    { id:264, parentID:4, Name:"Cheryl Pearson" }
                  ]
                })
            })
        });
        Try dragging Cheryl in between Gene and Olivier and you will find the tree does not update. If you drag Cheryl to become a child of Gene the tree will update as expected.

        Other things I've noticed with a bigger data set. Dragging an item that was initially several folders deep into some folder higher up the tree will insert it where expected, dragging into a sibling's folder always inserts at the bottom of the sibling's child list, even if I position it between two of the sibling's children.

        Drop the above code into the feature explorer to see what I'm dealing with.

        I would love if someone who has gotten this type scenario to work would show me how.

        Thanks,
        -Chris

        Comment


          #5
          If you want a persistent order you must have a field representing that persistent order, and you save, you need to update it on all the relevant records and then sort by that field once the data returns.

          You can alternatively form a Tree from data retrieved from the DataSource, and add drop handling code (override folderDrop) to do persistence yourself.

          Comment


            #6
            We are obviously experiencing some sort of communication problem.

            I stated in the first post that I have the whole server persistence issue working just fine. I have always had folderDrop() overridden to deal with the database persistence. And YES, I do call Super('folderDrop',arguments) inside my overridden folderDrop(). The debug output that I included in post #3 of this thread shows that I am returning the proper data, INCLUDING a proper order field, and that the cache is being properly updated.

            Upon including the 'order' field in the actual tree display (which isn't what I want, but was done to verify that in fact it was properly being set) I found that the order was in fact updated and that the tree did in fact see the change. However the tree does not properly sort itself when the data is updated.

            The problem is with the client not properly updating to show the correct ordering. The code in my last post was to demonstrate that even taking the persistent database code out of the equation doesn't solve the problem. Smart Client DOES NOT move the dragged folder to the desired location when re-ordering inside of the same parent when using a DataSource. It also has issues when dragging to a new parent too, since it tends to ignore the index value and just throws it at the end of the child list.

            You say that I should call sort() after the receiving the data. There are a couple of problems with this suggestion.

            1) The call to sort happens BEFORE the data gets returned from the server since the server call happens in a separate thread. The Super('folderDrop',arguments') call doesn't accept a callback function to call after data has been updated, and I haven't been able to find any kind of event to watch to know when it has happened. dataArrived() does not get called when updating the order, only when originally opening the node.

            2) As an ugly hack I tried calling this.delayCall('sort',['order'],5000,this); inside my overridden folderDrop() but that doesn't work either since the tree was already sorted by this value and seems to ignore any additional programmatic requests to sort by the same column in the same direction. So I end up needing to make 2 delayed calls to sort. The first to sort by some other column or direction, and the second to actually sort by the column and direction that I want. Of course this causes a crappy user experience, and doesn't even begin to address the race conditions that are put in place because of the delayed call.

            I would assume that contrary to the way everything else in SmartClient seems to work, that I must need to manually call updateData on the DataSource directly inside my folderDrop() call which would allow me to set a proper callback to deal with the need to manually re-sort the newly received data. Of course, I've read the forums (like this thread: http://forums.smartclient.com/showthread.php?t=2199&highlight=folderDrop) that say just call Super('folderDrop',arguments) and it will be taken care of (which is what I would expect given the rest of the apparent design of SmartClient). You will notice that your advice in that thread is:
            If you take a look at the docs for folderDrop you'll see that if your DataSources are declared correctly, you don't even need to implement a folderDrop() function, the appropriate DataSource operations will be initiated automatically.
            I still need to override folderDrop() because I need to pass some additional parameters to the server, and calling Super('folderDrop',arguments) does automatically call updateData for me, it just doesn't properly update the client tree view when I send back the modified data. It updates any modified fields properly, just not the ordering.

            The tone of Isomorphic's responses to me in this thread suggest that you think this is an easy thing to do, and that the problem is on my end. If it is so easy to do then please create an example in the feature explorer demonstrating how to do it.

            I have found numerous posts in these forums from people experiencing the same problem as I am having so obviously I am not in a unique situation and taking the time to create a proper example would be time well spent to avoid these questions in the forums.

            At a minimum at least copy and paste the code that I posted in my last message into the feature explorer right here on this site and verify that in fact SmartClient does not properly update the ordering.

            If it is a problem with my code then I would appreciate it if you would be specific in your response to how to fix it rather than vague 'check the faq' type responses. If for no other reason than it will give future users a reference to see a working example.

            I have built sortable tree's in ExtJS, YUI, Prototype/Scriptaculous, and Dojo, and I have NEVER had this much trouble getting the basic functionality to work.

            If you need to play with my exact code to verify the issue then send me an e-mail and I'll work out getting you access to my server so you can verify for yourself. Although as the code from my last post indicates, server code is not necessary to duplicate the problem.

            Thanks,
            -Chris

            Comment


              #7
              In the alternative approach previously mentioned of building a client-side Tree and calling a DataSource directly, you would not have a DataSource on the TreeGrid, you would not be calling TreeGrid.fetchData(), you would be calling DataSource.fetchData() and building the Tree from that and using setData() to apply it.

              At that point the existing Tree Reordering examples in the Feature Explorer show you everything you need to do except the persistent operations you perform on folderDrop (which will now include a direct call to updateData(), NOT having the TreeGrid do it for you). This is the implementation you seem to be closest to where you are now. This is the implementation equivalent to other frameworks.

              As far as letting SmartClient do the updateData operations for you, we're going to me making re-sorting automatic, but right now, yes you'd have to do the sort yourself, and note that calling sort() on the TreeGrid itself is going to no-op because it believes the sort direction has not changed. sortByProperty() on the Tree object itself will force a new sort.

              Comment


                #8
                Note further: allowing SmartClient to automatically issue updateData() calls is not going to work with a persistent order since SmartClient does not know to potentially issue multiple requests (one per node who's order field changed). Hence you would need to make your own call(s) to DataSource.updateData(), and the callback for this gives you a natural place to re-sort.

                Part of the disconnect was that we assumed you were not expecting a call to Super("folderDrop") to automatically persist given your usage.

                Comment


                  #9
                  I'm actually returning every node for the parent when any of its children are reordered, allowing the TreeGrid to update them all with the proper order. Which means I don't have a logical spot to insert the callback.

                  I would suggest having an additional callback parameter passed to folderDrop that would be called on completion of the update.

                  Currently folderDrop() simply calls transferNodes() which in turn calls
                  isc.DataBoundComponent._updateDataViaDataSource() which in turn calls updateData() with null specified for a callback.

                  I don't mind calling sortByProperty() on the tree object as long as I can call it at the right time. Right now everything is working properly except for the fact that there is no way to call sort() (or sortByProperty) at the appropriate time.

                  Doing what you suggest of manually calling updateData for each of the other nodes after SmartClient updates the one that I dragged automatically still doesn't work for the same reason that I can't call sort now. There is no guarantee that the result will be back from the server before I make the call to updateData for the siblings. And even if there was, if I don't have access to the response from the server from the automatic call then I don't know if it was successful or not, and so I can't make the updates.

                  And sending off individual updates one at a time doesn't work in a multi user environment where 2 people could potentially be modifying the same tree simultaneously.

                  The way I'm doing it now I pass the moved node ID's to the server along with the new position. I can then run all the updates in a transaction on the database to ensure that they either all pass, or all fail, and that there is no race condition with any simultaneous reordering by other users.

                  Using your suggestion of multiple server calls would invariably corrupt the ordering on the server.

                  As a side note, SmartClient could do it automatically since it already knows which nodes got moved where, and should then know which siblings needed to be adjusted. It shouldn't do it though since sending multiple requests would corrupt the server data given the simultaneous user scenario.

                  So really, the only real issue in making the existing system work as expected is needing the tree to resort itself when it gets a response back from the server with new data, using the existing sort field and direction.

                  It seems to me this should be brain dead simple to implement in the core SmartCode library as a callback inside transferNodes() that is passed to _updateDataViaDataSource and is subsequently set as the callback parameter to updateData.

                  This would allow every component that issues calls to _updateDataViaDataSource to specify its own callback if needed.

                  As a side note: I'm curious what the use case for having a sortable tree is if it isn't persisted somewhere.

                  Just for verification. You are saying that the code I posted above is the expected behavior? If so, why does the tree update if I move to a different parent, which really also involves reordering, but doesn't if I move within the same parent if it isn't a bug in SmartClient?

                  Thanks,
                  -Chris

                  Comment


                    #10
                    Various comments about multiple requests: that's what Queuing is for, see Server Examples -> Transactions.

                    Again, SmartClient has a built-in concept of what requests to issue for a *change of parent* but does not have a built-in concept of what request(s) to issue to update a *persistent, stored order*. There are a number of possible ways to implement a persistent order so built-in assumptions about how the order is stored are as likely to work as not in a given case.

                    So again calling Super() will not automatically issue requests that change the order. Since you have to issue your own request(s), you could use the callback from those requests to re-sort the tree.

                    Alternatively, as was previously suggested, by not supplying a DataSource to the TreeGrid you are then working with a local Tree and SmartClient will not automatically issue requests at all, but *will* rearrange the data client-side. This again is equivalent to what other frameworks offer - client-side data manipulation, add your own persistence.

                    Comment

                    Working...
                    X