Success! Looks like we've fixed this one. According to our records the fix was applied for EXTGWT-2644 in 3.0.4.
  1. #1
    Sencha User
    Join Date
    May 2011
    Location
    Netherlands
    Posts
    44
    Vote Rating
    1
    rcbeuker is on a distinguished road

      0  

    Default Data update bug in TreeGrid

    Data update bug in TreeGrid


    In my program I have a TreeGrid which is connected with live data via a RpcProxy and a TreeLoader.

    Schermafbeelding 2012-11-27 om 5.46.43 PM.png

    Now there's is a bug in the TreeGrid when the Parent 'Map' is open while there's a data update for this Parent 'Map'. For example in this situation 'Map A' when I make a copy of 'Doc A'. After the update of the data nothing seems to happen, but when 'Map A' is closed and opened again;

    Schermafbeelding 2012-11-27 om 5.57.05 PM.png

    The old 'Doc A' and the new 'Copy of Doc A' are present in 'Map A' (as it should) but there's also a new strange copy of 'Doc A' outside 'Map A'

    Schermafbeelding 2012-11-27 om 5.58.27 PM.png

    The new data outside the parent is random; sometimes only one file from the parent and sometimes multiple files from the parent. This behavior only occurs when the parent 'Map' is open while updating the data from this 'Map'. As a workaround my program closes every 'Map' before updating it's data. But this is a lot of work and is strange behavior to the user...

  2. #2
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,731
    Vote Rating
    90
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    Can you reproduce this on any of the samples? I'm not entirely clear on the steps, if this is something that can be run with either the editing or drag and drop examples.

    Which version of GXT are you using?

    Can you reproduce this in a code sample that we can test with?

    From the comments about 'a copy of' and updating an object, I'm concerned that there is a colliding key bug, but you should get assertion failures about that in dev mode.

  3. #3
    Sencha User
    Join Date
    May 2011
    Location
    Netherlands
    Posts
    44
    Vote Rating
    1
    rcbeuker is on a distinguished road

      0  

    Default


    Hello Colin,

    I'm using version; <gxt.version>3.0.1</gxt.version> with Maven.

    To refute your statement about colliding keys i enabled the key values in the TreeGrid (right side);

    Schermafbeelding 2012-11-28 om 10.38.52 AM.png

    This is before the copy/past action;

    Schermafbeelding 2012-11-28 om 10.40.09 AM.png

    This is after the copy/past action while closing 'Map A' while pasting, so the data update takes places on a closed parent. As you see, this works well and the new key is unique. Now I copy 'Copy of Doc A' again and leave 'Map A' open while pasting.

    Nothing happens on the screen (with de debugger i can see the nodes are updated as they should) ... But after closing 'Map A';

    Schermafbeelding 2012-11-28 om 10.41.50 AM.png

    The two old docs 'Doc A' and 'Copy of Doc A' are outside 'Map A', and when opening 'Map A' again;

    Schermafbeelding 2012-11-28 om 10.42.28 AM.png

    As you can see, the copy action succeeded with three unique keys (the first three documents). But the two old documents (the last two) appear as ghost documents outside 'Map A'

    Summarised; The TreeGrid does not update live data in the parent branch while a parent branch is open. When this parent branch is closed after this data update, the current members of this branch are viewed outside (next to) the parent branch (this is the bug). Inside the parent branch everything is ok. The ghost files are indeed with duplicated keys, but these do not originate from the server.

    Regards,

    Roland Beuker

  4. #4
    Sencha User
    Join Date
    Jul 2011
    Posts
    28
    Vote Rating
    1
    rbrecheis is on a distinguished road

      0  

    Default


    Hi,

    I'm not sure whether the duplicate nodes are really "outside" the parent map. They have the same indentation as the nodes which are supposedly "inside" the parent map. If they are really outside I would expect the duplicate node to have the same indentation as the parent map. So, I would see the following situation:

    Code:
    - Map A
          Doc A
          Copy of Doc A
      Doc A (this is the duplicate node...)
    The duplicate node is directly underneath the parent map. This is also how the context menu example shows the items in the GXT explorer demo. In the screenshots, however, I see that the duplicate node has the same indentation as the map's child nodes, which looks as follows:

    Code:
    - Map A
          Doc A
          Copy of Doc A
          Doc A (this is the duplicate node...)
    What's strange about all this is that when the parent map is closed/collapsed, the duplicate node is still visible, as follows:

    Code:
    + Map A
          Doc A (this is the duplicate node...)
    So, the duplicate node seems to be a child of the parent map (as intended), has same indented position as the other nodes, but for some strange reason is not hidden when the parent is collapsed. Of course, this duplicate node should not be there at all, regardless of its position, but this is just an observation that might trigger some ideas.

    Kind regards,

    Ralph

  5. #5
    Sencha User
    Join Date
    May 2011
    Location
    Netherlands
    Posts
    44
    Vote Rating
    1
    rcbeuker is on a distinguished road

      0  

    Default onDataChange does not trigger render/refresh methods

    onDataChange does not trigger render/refresh methods


    I've found the cause of the problem

    Nothing happens after the data update because the GridView is not updated (onUpdate, doRender, renderUI, renderRows, refreshRow etc.) after a data update from the server... The onDataChange(M parent) from TreeGrid does not trigger any of these methods...

    Following on this, the methods from GridView are also not called when the parent branche (for example Map A) is closed after the data update. This causes the 'ghost Doc A' outside Map A after the data update and parent collapse;

    Schermafbeelding 2012-11-27 om 5.46.43 PM.png

    Attachment 40486

    There's no render/refresh so Doc A just stays where it already was, only the '-' from the parent Map A changes into '+'. This is actually the same situation as before;

    Finaly Map A is expanded again, for the first time this triggers GridView methods;

    onAdd()
    insertRows(int firstRow, int lastRow, boolean isUpdate)
    renderRows(int startRow, int endRow)

    The rendering starts on the index of Map A and ends on the index of Copy of Doc A leaving the old 'ghost Doc A' untouched

    Schermafbeelding 2012-11-27 om 5.57.05 PM.png

    There must be an additional function which renders the GridView straight after the data update onDataChange(M parent)

  6. #6
    Sencha User
    Join Date
    Jul 2011
    Posts
    28
    Vote Rating
    1
    rbrecheis is on a distinguished road

      0  

    Default


    Nice work

  7. #7
    Sencha User
    Join Date
    May 2011
    Location
    Netherlands
    Posts
    44
    Vote Rating
    1
    rcbeuker is on a distinguished road

      0  

    Default


    Now i need to know how to start the additional render/refresh after the data update

  8. #8
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,731
    Vote Rating
    90
    Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light Colin Alworth is a glorious beacon of light

      0  

    Default


    Quote Originally Posted by rcbeuker View Post
    Nothing happens after the data update because the GridView is not updated (onUpdate, doRender, renderUI, renderRows, refreshRow etc.) after a data update from the server... The onDataChange(M parent) from TreeGrid does not trigger any of these methods...
    Maybe I'm missing something (and still without an example or how to modify our own examples I'm going to be missing things until I get lucky reproducing this), but TreeGrid.onDataChange may not call those things, but it does appear to update the ListStore that represents the actual set of items to be rendered - this happens via the TreeGrid.renderChildren method. TreeGridView, which via GridView listens to that underlying ListStore should be listening to those events, should then respond through its own remove/add events. Any StoreDataUpdateEvent coming from the TreeStore should therefore be causing a bunch of add/remove events on the ListStore, which the GridView/TreeGridView should be reading, and redrawing accordingly.

    Quote Originally Posted by rcbeuker View Post
    Following on this, the methods from GridView are also not called when the parent branche (for example Map A) is closed after the data update. This causes the 'ghost Doc A' outside Map A after the data update and parent collapse;

    Attachment 40487

    Attachment 40486

    There's no render/refresh so Doc A just stays where it already was, only the '-' from the parent Map A changes into '+'. This is actually the same situation as before;

    Finaly Map A is expanded again, for the first time this triggers GridView methods;

    onAdd()
    insertRows(int firstRow, int lastRow, boolean isUpdate)
    renderRows(int startRow, int endRow)

    The rendering starts on the index of Map A and ends on the index of Copy of Doc A leaving the old 'ghost Doc A' untouched

    Attachment 40488

    There must be an additional function which renders the GridView straight after the data update onDataChange(M parent)
    How is the rest of this wired up? How are changes coming from the client, to the server, and back to the client? This behavior (name changes, etc) doesn't seem to be any of the standard loaders, so I'm assuming something else is in place. Then how are you wiring the load event to the TreeStore? Two provided classes that can do this for you are SubTreeStoreBinding and ChildTreeStoreBinding, based on how much of the subtree you are rewiring.

    The initial post mentioned "a bug in the TreeGrid when the Parent 'Map' is open while there's a data update for this Parent 'Map'", but now it appears to be the data changed events which cause this issue - is it both, or just the one?

    To be clear, I certainly believe there is a bug here, and I could believe that the bug is within GXT itself, but without a sketch of how updates are being performed, it is very difficult to say. http://www.sencha.com/examples/#Exam...itabletreegrid allows a user to cause the StoreUpdateEvents (actually, StoreRecordChangedEvent, but set autoCommit to true on the TreeStore, and you get StoreUpdateEvents), which don't seem to have the issue described. Initial experimentation with firing a real StoreDataChangedEvent doesn't seem to cause an issue either:

    Code:
        store.addStoreUpdateHandler(new StoreUpdateHandler<BaseDto>() {
          @Override
          public void onUpdate(final StoreUpdateEvent<BaseDto> event) {
            Scheduler.get().scheduleDeferred(new ScheduledCommand() {
              @Override
              public void execute() {
                store.fireEvent(new StoreDataChangeEvent<BaseDto>(event.getItems().get(0)));
              }
            });
          }
        });
    Your test description and images certainly don't sound like expected behavior, but we have the bug template for a reason - until we've got a test case that lets us reproduce the issue, we can't be sure we've fixed it, even following the best descriptions.

    I'll continue to try to reproduce it, but lacking reproducible examples, I'm a little limited.

    Now i need to know how to start the additional render/refresh after the data update
    Perhaps listen to the DataChangedEvent, and defer a call to TreeGrid.refresh?

  9. #9
    Sencha User
    Join Date
    May 2011
    Location
    Netherlands
    Posts
    44
    Vote Rating
    1
    rcbeuker is on a distinguished road

      0  

    Default The complete story

    The complete story


    Hello Colin,

    Thanks for your extensive attention, and my apologies for the missing bug template in this story; where're facing a strict deadline in December and this bug involves a lot of code.

    How is the rest of this wired up? How are changes coming from the client, to the server, and back to the client? This behavior (name changes, etc) doesn't seem to be any of the standard loaders, so I'm assuming something else is in place.
    We are using the TreeGrid as a kind of a file system for files from a remote server. Therefore we extended a RpcProxy and a TreeLoader and connected this to the TreeGrid with a ChildTreeStoreBinding. The LoadConfig from the RpcProxy is always a parent directory (starting from the root directory map) and the file server always returns a childlist from the given LoadConfig/Parent. This is actually working quit well when opening and viewing existing files (caching is enabled so the child files are only loaded at the first opening of the Parent).

    Then how are you wiring the load event to the TreeStore? Two provided classes that can do this for you are SubTreeStoreBinding and ChildTreeStoreBinding, based on how much of the subtree you are rewiring.
    We choose for the ChildTreeStoreBinding is this the best choise for our use-case (or should we switch to the SubTreeStoreBinding)?

    From this point it's only standard Sencha code. Now a failing use-case; COPY/PASTE

    Schermafbeelding 2012-11-27 om 5.46.43 PM.png

    I open Map A, select Doc A and choose COPY. This only saves the File_ID from Doc A. Then i select Map A and choose PASTE. This results in a copy command to the server which creates a Copy of Doc A (in Map A) and returns Map A as an updated Parent directory to our RpcProxy/TreeLoader. Our TreeLoader calls the standard load(pChangedParentItem); which results in a new list of children for Map A to the TreeGrid;

    replaceChildren(M parent, List<M> children)
    parentNode.addChildren(0, wrap(children))
    fireEvent(new StoreDataChangeEvent<M>(parent))
    onDataChange(M parent)
    renderChildren(parent, autoLoad)
    register(M m)
    register(M m) (only 'Copy of Doc A' is added to nodes)
    refresh(parent)
    treeGridView.onIconStyleChange(node, calculateIconStyle(model))
    treeGridView.onJointChange(node, calculateJoint(model))


    Thats all; no call to GridView ...

  10. #10
    Sencha User
    Join Date
    May 2011
    Location
    Netherlands
    Posts
    44
    Vote Rating
    1
    rcbeuker is on a distinguished road

      0  

    Default renderChildren only updates the nodes, not the store ...

    renderChildren only updates the nodes, not the store ...


    ... but it does appear to update the ListStore that represents the actual set of items to be rendered - this happens via the TreeGrid.renderChildren method. TreeGridView, which via GridView listens to that underlying ListStore should be listening to those events, should then respond through its own remove/add events. Any StoreDataUpdateEvent coming from the TreeStore should therefore be causing a bunch of add/remove events on the ListStore, which the GridView/TreeGridView should be reading, and redrawing accordingly.
    The previous post shows the trace from replaceChildren(M parent, List<M> children) ;

    replaceChildren(M parent, List<M> children)
    parentNode.addChildren(0, wrap(children))
    fireEvent(new StoreDataChangeEvent<M>(parent))
    onDataChange(M parent)
    renderChildren(parent, autoLoad)
    register(M m)
    register(M m) (only 'Copy of Doc A' is added to nodes)
    refresh(parent)
    treeGridView.onIconStyleChange(node, calculateIconStyle(model))
    treeGridView.onJointChange(node, calculateJoint(model))

    Indeed, renderChildren(parent, autoLoad) is called with parent = 'Map A' and autoLoad = 'false'
    Code:
    protected void renderChildren(M parent, boolean auto) 
    {
      List<M> children = parent == null ? treeStore.getRootItems() : treeStore.getChildren(parent);
    
      for (M child : children) 
      {
        register(child);
      }
    
     if (parent == null && children.size() > 0)  // parent != null
        {
          store.addAll(children);
        }
    
        for (M child : children)
        {
          if (autoExpand) // autoExpand == false
          {
            final M c = child;
            Scheduler.get().scheduleDeferred(new ScheduledCommand()
            {
              @Override
              public void execute() 
              {
                setExpanded(c, true);
              }
            });
          } 
         else if (loader != null) 
         {
            if (autoLoad) // autoLoad == false
            {
               if (store.isFiltered() || (!auto))
                {
                  renderChildren(child, auto);
                } 
                else
                {
                  loader.loadChildren(child);
                }
             }
          }
        }
      }
    
    As you can see, nothing happens to the store in this method ...

Thread Participants: 2