Thank you for reporting this bug. We will make it our priority to review this report.
  1. #1
    Ext GWT Premium Member
    Join Date
    Jun 2010
    Location
    Kyrksæterøra, Norway
    Posts
    68
    Vote Rating
    2
    stigrv is on a distinguished road

      0  

    Default TreeStore insert

    TreeStore insert


    The insert method of the TreeStore method does not add child elements to the modelMap. This method is called from the add(parent, children) method, which should be used for adding elements to the tree as far as I can see. The result of this map not being updated is that the first level of elements gets added as they should, although lookups will not work. If I then want to add children to one of the children added in the first step, I'm told that the model does not exist in the TreeStore. The getAll method does however hold these records if I want to do a manual iteration, but that won't help me in adding the child elements I want to add.

  2. #2
    Sencha - GXT Dev Team
    Join Date
    Feb 2009
    Location
    Minnesota
    Posts
    2,732
    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


    I'm not sure I understand how insert could not work, yet add could - all add() methods directly call one insert() method or another, and all of those methods end up in either
    Code:
    private void insert(TreeModel parent, int index, M child)
    or
    Code:
    private void insert(TreeModel parent, int index, List<M> children)
    Each of these then uses wrap(M) to generate a TreeModel wrapper around that newly added M object. This wrap(M) method then is responsible for adding an item to the modelMap for later use.

    Any subsequent operation to look up items later uses getWrapper to find that item - if it isn't present, getWrapper will (in dev mode) throw an assertion to point out the problem, because this must be avoided.

    You can verify that an item is in the modelMap with TreeStore.findModelWithKey, which uses that modelMap to look up items.

    In this sample, I'm testing both getAll().size() and getAllItemsCount() - both follow the same basic steps, but getAllItemsCount() includes an assert that the total number of items in the tree structure matches the items in the modelMap. If that assertion ever goes off, something fundamental has broken in the tree's bookkeeping.

    Code:
    public class Test implements EntryPoint {
    
      public void onModuleLoad() {
        SampleDataProperties props = GWT.create(SampleDataProperties.class);
    
        final TreeStore<SampleData> treeStore = new TreeStore<SampleData>(props.id());
    
        final SampleData root = new SampleData("Root", "The top of the tree");
        SampleData parentAndChild = new SampleData("parent and child", "The first child of root, has children");
        SampleData leafNodeA = new SampleData("Leaf Node A", "The only 3rd tier child in this tree");
        SampleData leafNodeB = new SampleData("Leaf Node B", "Another entry in the tree");
        SampleData leafNodeC = new SampleData("Leaf Node C", "Last entry in the tree");
    
        treeStore.add(root);
        treeStore.add(root, parentAndChild);
        treeStore.add(root, leafNodeC);
        treeStore.add(parentAndChild, leafNodeA);
    
    
        //Count number of items:
        System.out.println("Total number of items with getAllItemsCount (4): " + treeStore.getAllItemsCount());
        System.out.println("Total number of items with getAll.size (4): " + treeStore.getAll().size());
        for (SampleData item : treeStore.getAll()) {
          //check that each item is in the modelMap
          System.out.print("  " + item.getId() + ": " + item);
          System.out.println(" -- " + treeStore.findModelWithKey(item.getId() + ""));
        }
        System.out.println();
    
    
        //insert an item:
        treeStore.insert(root, 1, Collections.singletonList(leafNodeB));
        
        
        //Again, count number of items:
        System.out.println("Total number of items with getAllItemsCount (5): " + treeStore.getAllItemsCount());
        System.out.println("Total number of items with getAll.size (5): " + treeStore.getAll().size());
        for (SampleData item : treeStore.getAll()) {
          //check that each item is in the modelMap
          System.out.print("  " + item.getId() + ": " + item);
          System.out.println(" -- " + treeStore.findModelWithKey(item.getId() + ""));
        }
        System.out.println();
      }
    
      public interface SampleDataProperties extends PropertyAccess<SampleData> {
        ModelKeyProvider<SampleData> id();
    
        ValueProvider<SampleData, String> name();
        ValueProvider<SampleData, String> description();
      }
      public static class SampleData {
        private static int nextId = 0;
    
        private final int id = nextId++;
    
        private String name;
        private String description;
    
        public SampleData(String name, String description) {
          setName(name);
          setDescription(description);
        }
    
        public int getId() {
          return id;
        }
    
        public String getName() {
          return name;
        }
    
        public void setName(String name) {
          this.name = name;
        }
    
        public String getDescription() {
          return description;
        }
    
        public void setDescription(String description) {
          this.description = description;
        }
    
        @Override
        public String toString() {
          return "name: " + name;
        }
      }
    }
    Can you clarify this bug, and perhaps provide a change to my sample or another working sample that demonstrates the issue?

  3. #3
    Ext GWT Premium Member
    Join Date
    Jun 2010
    Location
    Kyrksæterøra, Norway
    Posts
    68
    Vote Rating
    2
    stigrv is on a distinguished road

      0  

    Default


    After digging around some more I found that the problem was related to how the tree component expands its nodes. When you call the setExpanded method, the child nodes of the node set to be expanded will be removed from the store. This can be seen in the onExpand method of the tree. The second block here calls store.removeChildren(model). This probably makes sense if you want to expand nodes manually down from the root down to the node you want to look at.

    What I was trying to achieve was to load the full content down the tree to a node that should be selected. I start by adding the first node missing from the root level down to the node I want to have selected, and then each level down to this one node. When the add children to a node method is called manually, one should probably set the loaded flag of the TreeNode class to true, to avoid the children nodes to be removed.

    This code works as it should if you disconnect the loader from the tree. Please excuse some of the weird code here, I was trying to emulate the data layer in our application.

    Code:
    public class TreeStoreInsert implements EntryPoint {
    
    
        private TreeStore<HelpDto> store;
        private Tree<HelpDto, String> tree;
    
    
        @Override
        public final void onModuleLoad() {
            this.store = new TreeStore<HelpDto>(new HelpDtoKeyProvider());
    
    
            final RpcProxy<HelpDto, List<HelpDto>> proxy = new HelpProxy();
            final TreeLoader<HelpDto> loader = new HelpLoader(proxy);
            loader.addLoadHandler(new ChildTreeStoreBinding<HelpDto>(this.store));
    
    
            this.tree = new Tree<HelpDto, String>(
                    this.store, new HelpDtoValueProvider());
            this.tree.setLoader(loader);
            RootPanel.get().add(this.tree);
    
    
            System.out.println("Initial count: " + this.store.getAllItemsCount());
    
    
            final TextButton button = new TextButton("Populate");
            button.addSelectHandler(new SelectHandler() {
                @Override
                public void onSelect(final SelectEvent event) {
                    TreeStoreInsert.this.populate();
                }
            });
            RootPanel.get().add(button);
        }
    
    
        private void populate() {
            final HelpDto firstChild = this.store.getFirstChild(null);
            System.out.println("First child: " + firstChild);
    
    
            final HelpDto subChild = new HelpDto() {
                {
                    this.setId("sub1");
                    this.setParentId(firstChild.getId());
                    this.setTitle("sub1");
                }
            };
    
    
            firstChild.setChildCount(1);
            this.store.add(firstChild, Arrays.asList(subChild));
            System.out.println("First sub count: " + this.store.getAllItemsCount());
            this.tree.setExpanded(firstChild, true);
    
    
            final HelpDto subSubChild = new HelpDto() {
                {
                    this.setId("subSub1");
                    this.setParentId(subChild.getId());
                    this.setTitle("subSub1");
                }
            };
    
    
            subChild.setChildCount(1);
            this.store.add(subChild, Arrays.asList(subSubChild));
            System.out.println("Second sub count: "
                    + this.store.getAllItemsCount());
            this.tree.setExpanded(subChild, true);
        }
    
    
        public static class HelpDto {
            private String id;
            private String parentId;
            private String title;
            private int childCount;
    
    
            public final String getId() {
                return this.id;
            }
    
    
            public final void setId(final String id) {
                this.id = id;
            }
    
    
            public final String getParentId() {
                return this.parentId;
            }
    
    
            public final void setParentId(final String parentId) {
                this.parentId = parentId;
            }
    
    
            public final String getTitle() {
                return this.title;
            }
    
    
            public final void setTitle(final String title) {
                this.title = title;
            }
    
    
            public final int getChildCount() {
                return this.childCount;
            }
    
    
            public final void setChildCount(final int childCount) {
                this.childCount = childCount;
            }
    
    
            @Override
            public final String toString() {
                return this.id + ": " + this.title;
            }
        }
    
    
        public static class HelpDtoKeyProvider implements ModelKeyProvider<HelpDto> {
            @Override
            public final String getKey(final HelpDto item) {
                return item.getId();
            }
        }
    
    
        public static class HelpDtoValueProvider
                implements ValueProvider<HelpDto, String> {
            @Override
            public final String getValue(final HelpDto object) {
                return object.getTitle();
            }
    
    
            @Override
            public void setValue(final HelpDto object, final String value) {
            }
    
    
            @Override
            public final String getPath() {
                return null;
            }
        }
    
    
        private class HelpProxy extends RpcProxy<HelpDto, List<HelpDto>> {
    
    
            private int index;
    
    
            @Override
            public void load(final HelpDto parentHelp,
                    final AsyncCallback<List<HelpDto>> callback) {
                final List<HelpDto> result = new ArrayList<HelpDto>();
                for (int i = 0; i < 3; i++) {
                    result.add(new HelpDto() {
                        {
                            this.setId(String.valueOf(HelpProxy.this.index++));
                            this.setParentId(parentHelp != null ?
                                    parentHelp.getId() : null);
                            this.setTitle(this.getId());
                        }
                    });
                }
                callback.onSuccess(result);
            }
        }
    
    
        private class HelpLoader extends TreeLoader<HelpDto> {
    
    
            public HelpLoader(final DataProxy<HelpDto, List<HelpDto>> proxy) {
                super(proxy);
            }
    
    
            @Override
            public boolean hasChildren(final HelpDto parent) {
                return (parent.getChildCount() > 0);
            }
    
    
            @Override
            protected void onLoadSuccess(
                    final HelpDto loadConfig, final List<HelpDto> result) {
                super.onLoadSuccess(loadConfig, result);
            }
        }
    }

  4. #4
    Ext GWT Premium Member
    Join Date
    Jun 2010
    Location
    Kyrksæterøra, Norway
    Posts
    68
    Vote Rating
    2
    stigrv is on a distinguished road

      0  

    Default


    It is also possible to set the loaded state manually, and the following change makes the test case work.

    Code:
            firstChild.setChildCount(1);
            this.store.add(firstChild, Arrays.asList(subChild));
            System.out.println("First sub count: " + this.store.getAllItemsCount());
            this.tree.findNode(firstChild).setLoaded(true);
            this.tree.setExpanded(firstChild, true);

Thread Participants: 1