Tree - is it possible to lazy load the children indicator ?

Hi,
I’m currently trying to render a tree with a behaviour similar to Windows Explorer:

  • WinExplorer first displays a plus sign on every folder (at least on XP and on slow/network devices).
  • Only when the user opens a folder (by clicking on the plus sign), explorer checks wether there are folder children and if not, it immediately removes the plus sign so that the user sees that there is nothing to expand anymore.

I have written a hierarchical container for my tree and for performance reasons I cannot peek into every folder just to be able to tell vaadin if a certain tree item hasChildren(). So initially I return true for every tree item.
However once a user opens/expands an item, I fetch the children and if there are none, I want the small arrow indicator to vanish.
Unfortunately I saw that when the user expands a tree item, vaadin first asks my container if it hasChildren() for that item and only afterwards invokes the getChildren() method.

Is it possible to achieve the described behaviour (though I fear that it is not :frowning: ) ?

Regards,
DG

There’s no built-in lazy loading in Tree, but what you want is possible using a Tree.ExpandListener as follows:

// Have a tree with some unexpanded root items
final Tree tree = new Tree("My Tree");
tree.addItem("One Node");
tree.addItem("Another Node");
tree.addItem("Third Node");

// When an item is expanded, add some children
tree.addListener(new Tree.ExpandListener() {
    int childCounter = 0;

    public void nodeExpand(ExpandEvent event) {
        // No children for the first node
        if (event.getItemId().equals("One Node")) {
            tree.setChildrenAllowed(event.getItemId(), false);
            
            getWindow().showNotification("No children");
        } else {
            // Add a few new child nodes to the expanded node
            for (int i=0; i < 3; i++) {
                String childId = "Child " + (++childCounter);
                tree.addItem(childId);
                tree.setParent(childId, event.getItemId());
            }
    
            getWindow().showNotification("Added nodes");
        }
    }
});

// When an item is collapsed, remove all children
tree.addListener(new Tree.CollapseListener() {
    public void nodeCollapse(CollapseEvent event) {
        // Remove all children of the collapsing node
        removeItemsRecursively(tree, event.getItemId());

        getWindow().showNotification("Removed nodes");
    }
    
    void removeItemsRecursively(Tree tree, Object item) {
        // Find all child nodes of the node
        Collection<?> children = tree.getChildren(item);
        if (children == null)
            return;
        
        // The list is unmodifiable so must copy to another list
        LinkedList<String> children2 = new LinkedList<String>();
        for (Iterator<?> i = children.iterator(); i.hasNext();)
            children2.add((String) i.next());
        
        // Remove the children of the collapsing node
        for (Iterator<String> i = children2.iterator(); i.hasNext();) {
            String child = i.next();
            removeItemsRecursively(tree, child);
            tree.removeItem(child);
        }
    }
});
        
layout.addComponent(tree);

See a
live example

In your situation, as you have a custom HierarchicalContainer, you would need a bit different solution that integrates with the custom container, but I guess it would also need the ExpandListener.

Hi Marko,

thank you very much for pushing me to the right track!
Indeed the expand listener will help, as it is invoked before vaadin asks my container about the children status
So have to implement the expand listener → trigger a children fetch in my container → remember the children in the container → respond correctly in my container when vaadin asks for the childrenAllowed status.

Thanks!

Adding children dynamically with Vaadin by expansion-event seems to be impossible for
TreeTable
.

Reason is following Vaadin
HierarchicalContainer
implementation:

[code]
@Override
public Item addItem(Object itemId) {

        final Item item = super.addItem(itemId);
        ....
        roots.add(itemId);
        ....
        return item;
     ....
}

[/code]So, when you use expansion-events to add children to nodes, you always modify the
roots
Collection by calling
addItem()
.

Now look at this
TreeTable.HierarchicalStrategy
implementation, which works when an expansion event is dispatched:

        private List<Object> getPreOrder() {
            ....
                Collection<?> rootItemIds = getContainerDataSource().rootItemIds();
                for (Object id : rootItemIds) {
                    preOrder.add(id);
                    addVisibleChildTree(id);
                }
            ....
        }

When you add children dynamically in
addVisibleChildTree(id)
, the[b]

[/b]
java.util.Iterator.next()
[b]

[/b]implementation will throw an

ConcurrentModificationException
here.

Because there is no
Vaadin API
call that immediately lets define a parent for an item, it seems to be impossible to add items during an expansion event.

As I got “IllegalStateException: A connector should not be marked as dirty while a response is being written” when trying to override
HierarchicalContainer.getChildren()
[b]

[/b], I still do not know how to add children dynamically with Vaadin - ???

The only workaround I found was overriding
HierarchicalContainer.rootItemIds()
and creating a copy of the root-item-ids:

    /** Override to avoid ConcurrentModificationException. */
    @Override
    public Collection<?> rootItemIds() {
        return new ArrayList<>(super.rootItemIds());
    }

Please, Vaadin programmers, create at least one new API method that lets add a tree child and pass a parent immediately, like this:

  public Object addItem(Object itemId, Object parentItemId)

The implementation of this new method should NOT touch the
roots
collection!