Treegrid: Collapsing immediatly on Open Node and or scrolling

Hi,

i am currently developing an application to view filestructures in a hierarchical way.
Therefore im using a TreeGrid component with lazy load.

When trying to open some nodes on the tree it does load the subfolders, shows them for almost 1 second and then reloads the root and all branches are collapsed again.
Currently i am using Vaadin 8.9.3.

Building the Treegrid

		HorizontalLayout horrlayout = new HorizontalLayout();
		horrlayout.setSizeFull();
		VerticalLayout vertLayout = new VerticalLayout();
		
		selectionState = SelectionState.getInstance();
		this.setSizeFull();
		List<FolderStatus> folderStatusDD = new ArrayList<>();
		folderStatusDD.add(FolderStatus.DS_RELEVANT);
		folderStatusDD.add(FolderStatus.NICHT_DS_RELEVANT);
		folderStatusDD.add(FolderStatus.KEINE_ARCHIVIERUNG);

		folderStatusCB.setWidth(100, Unit.PERCENTAGE);
		folderStatusCB.setItems(folderStatusDD);
		//Folder dbfolder = OrdnerkennungDAO.getFolderList("c_dsgvo_folders");
		final HierarchicalDataProvider<Folder, Void> dataProvider =  new AbstractBackEndHierarchicalDataProvider<Folder, Void>() {

			/**
			 * 
			 */
			private static final long serialVersionUID = -2038918474553295909L;

			@Override
			public int getChildCount(HierarchicalQuery<Folder, Void> query) {
				try {
					return OrdnerkennungDAO.getChilderFolderCount(query.getParent(), dbConnection);
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				return 0;
			}

			@Override
			public boolean hasChildren(Folder item) {
				try {
					return OrdnerkennungDAO.hasChildrenFolder(item, dbConnection);
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				return false;
			}

			@Override
			protected Stream<Folder> fetchChildrenFromBackEnd(HierarchicalQuery<Folder, Void> query) {
				try {
						
					return  OrdnerkennungDAO.getChildFolder(query.getParent(), dbConnection).stream();
				} catch (SQLException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				final List<Folder> list = Collections.emptyList();
				return list.stream();
			}
			
			
		};
		folderTreeGrid.addColumn(Folder::getDirName).setCaption("M:/");
		folderTreeGrid.setDataProvider(dataProvider); // setzten der Werte im Grid
		folderTreeGrid.setWidth(100, Unit.PERCENTAGE);
		folderTreeGrid.setHeight(100, Unit.PERCENTAGE);
		horrlayout.addComponent(folderTreeGrid);
        horrlayout.addComponent(vertLayout);
        horrlayout.setExpandRatio(folderTreeGrid, 2.0f);
        horrlayout.setExpandRatio(vertLayout, 1.0f);
        addComponent(horrlayout);

DAO

	public static int getChilderFolderCount(Folder folder, DatabaseConnectionManager dbConnection) throws SQLException {
		final Integer anzahl;
		final Connection conn = dbConnection.getDBConnection().getConnection();
		final StringBuilder sb = new StringBuilder();
		sb.append("select count(*) as anzahl from C_DSGVO_FOLDERS fold where fold.parent_dir = ?");
		try(PreparedStatement stmt = conn.prepareStatement(sb.toString())) {
			final Integer parentID = (folder != null) ? folder.getId() : 1;
			stmt.setInt(1, parentID);
			try(final ResultSet rs = stmt.executeQuery()){
				if(rs.next()) {
					anzahl = rs.getInt("anzahl");
				} else {
					anzahl = 0;
				}
			}
		}
		System.out.println("folder: "+(folder != null ? folder.getDirName(): "root")+"-> Children: "+anzahl);
		return anzahl;
	}

	public static boolean hasChildrenFolder(Folder folder, DatabaseConnectionManager dbConnection ) throws SQLException {
		return OrdnerkennungDAO.getChilderFolderCount(folder, dbConnection) > 0;
	}

	public static List<Folder> getChildFolder(Folder folder, final DatabaseConnectionManager dbConnection) throws SQLException {
		final LinkedList<Folder> folderList = new LinkedList<Folder>();
		final StringBuilder sb = new StringBuilder();
		sb.append("select folders.id, folders.dir, folders.status, level, folders.parent_dir, folders.user_kommentar from C_DSGVO_FOLDERS folders where folders.parent_dir = ? ")
		  .append("connect by prior folders.id = folders.parent_dir ")
		  .append("start with folders.parent_dir is null ")
		  .append("order siblings by folders.dir ");
		final Connection conn = dbConnection.getDBConnection().getConnection();
		try(PreparedStatement stmt = conn.prepareStatement(sb.toString())) {
			final Integer parentID = (folder != null) ? folder.getId() : 1;			
			stmt.setInt(1, parentID);
			try(final ResultSet rs = stmt.executeQuery()){
				while(rs.next()) {
        			final Integer id = rs.getInt("id");
        			final Integer status = rs.getInt("status");
        			final String kommentar = rs.getString("user_kommentar");
        			final Integer level = rs.getInt("level");
        			final String dir = rs.getString("dir");
        			final Folder f = new Folder(dir, folder, id, FolderStatus.getStatus(status), kommentar, level);
        			folderList.add(f);
	        	}
			}
		}
		
		return folderList;
	}

Folder

public class Folder {
	final private String dirName;
	final private List<Folder> subFolder;
	final private int id;
	private FolderStatus status;
	private String kommentar;
	private Integer level;
	private Folder parent;
	
	public Folder(final String path, final Folder parent, final Integer id, final FolderStatus status, final String kommentar, final Integer level) {
		super();
		this.dirName = path;
		this.id = id;
		this.parent = parent;
		this.status = status;
		this.kommentar = kommentar;
		this.subFolder = new LinkedList<>();
		this.level = level;
	}
	
	public FolderStatus getStatus() {
		return status;
	}
	
	public String getDirName() {
		return dirName;
	}

	public int getId() {
		return id;
	}
	public String getAbsolutePath() {
		final StringBuilder sb = new StringBuilder();
		if(this.parent != null) {
			sb.append(parent.getAbsolutePath());
		}
		sb.append("\\").append(this.getDirName());
		return sb.toString();
	}
	
	public void setParent(Folder parent) {
		this.parent = parent;
	}

	public Folder getParent() {
		return parent;
	}

	public List<Folder> getSubFolder() {
		return subFolder;
	}
	public void addSubFolder(final Folder subFolder) {
		this.subFolder.add(subFolder);
	}

	public void setStatus(FolderStatus status) {
		this.status = status;
	}

	public String getKommentar() {
		return kommentar;
	}

	public void setKommentar(String kommentar) {
		this.kommentar = kommentar;
	}

	public Integer getLevel() {
		return level;
	}

	public void setLevel(Integer level) {
		this.level = level;
	}

	@Override
	public String toString() {
		final StringBuilder sb = new StringBuilder();
		sb.append(this.getId()+"-" + dirName);
		for (final Folder folder : subFolder) {
			sb.append("\n ").append(folder);
		}
		return sb.toString(); 
	}

	@Override
	public boolean equals(Object obj) {
		
		if(this == obj) {
			return true;
		}
		if(obj == null) {
			return false;
		}
		if(getClass() != obj.getClass()) {
			return false;
		}
		final Folder f = (Folder) obj;
		if(this.dirName.equals(f.dirName) ) {
			if(this.getParent() == null && f.getParent() == null) {
				return true;
			}
			if(this.getParent().equals(f.getParent())) {
				return true;
			} else {
				return false;
			}
		}
		
		return super.equals(obj);
	}

	final public Folder getRoot() {
		if(this.getParent() == null) {
			return this;
		}
		return this.getParent().getRoot();
	}

Are you able to reduce your code example to a smaller size and modify it so that it works as standalone? Here are some tips: https://stackoverflow.com/help/minimal-reproducible-example

Hi,

I did reduce the code.
You may run the code now on your own.
It just adds 9 Folders all the time.
After opening the 4-5 level the structure collapses.

MyUI:

package com.example.myapplication;

import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

import javax.servlet.annotation.WebServlet;

import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.data.provider.AbstractBackEndHierarchicalDataProvider;
import com.vaadin.data.provider.HierarchicalDataProvider;
import com.vaadin.data.provider.HierarchicalQuery;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.TreeGrid;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;

@Theme("mytheme")
public class MyUI extends UI {

	private TreeGrid<Folder> folderTreeGrid = new TreeGrid<>();
	List<Folder> expanded = Collections.emptyList();
    @Override
    protected void init(VaadinRequest vaadinRequest) {
        final VerticalLayout layout = new VerticalLayout();
        layout.setSizeFull();
    	final HorizontalLayout horrlayout = new HorizontalLayout();
		horrlayout.setSizeFull();
		this.setSizeFull();
		
		/*
		 * Treegrid Lazy Loading
		 */
		final HierarchicalDataProvider<Folder, Void> dataProvider =  new AbstractBackEndHierarchicalDataProvider<Folder, Void>() {

			private static final long serialVersionUID = -2038918474553295909L;

			@Override
			public int getChildCount(HierarchicalQuery<Folder, Void> query) {
					return OrdnerkennungDAO.getChildFolderCount(query.getParent());

			}

			@Override
			public boolean hasChildren(Folder item) {
					return OrdnerkennungDAO.hasChildFolder(item);
			}

			@Override
			protected Stream<Folder> fetchChildrenFromBackEnd(HierarchicalQuery<Folder, Void> query) {
						
					return  OrdnerkennungDAO.getChildFolder(query.getParent()).stream();
			}
			
		};
		folderTreeGrid.addColumn(Folder::getDirName).setCaption("M:/");
		folderTreeGrid.setDataProvider(dataProvider);
		folderTreeGrid.setWidth(100, Unit.PERCENTAGE);
		folderTreeGrid.setHeight(100, Unit.PERCENTAGE);
		horrlayout.addComponent(folderTreeGrid);
        horrlayout.setExpandRatio(folderTreeGrid, 2.0f);
        layout.addComponent(horrlayout);
        setContent(layout);
    }

    @WebServlet(urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true)
    @VaadinServletConfiguration(ui = MyUI.class, productionMode = false)
    public static class MyUIServlet extends VaadinServlet {
    }
}

DAO:

package com.example.myapplication;

import java.util.LinkedList;
import java.util.List;

public class OrdnerkennungDAO {
	/*
	 * Just go on
	 */
	public static int getChildFolderCount(Folder parent) {
		return 1;
	}

	/*
	 * Just go on
	 */
	public static boolean hasChildFolder(Folder item) {
		return true;
	}

	/*
	 * Adds 9 Folders into the List
	 */
	public static List<Folder> getChildFolder(Folder parent) {
		final LinkedList<Folder> folderList = new LinkedList<Folder>();
		for(int i=0;i<10;i++) {
			final Folder f = new Folder("dir" +i, parent, i);
			folderList.add(f);
		}
		return folderList;
	}

}

Folder:

package com.example.myapplication;

public class Folder {
	final private String dirName;
	final private int id;
	private Folder parent;
	
	public Folder(final String path, final Folder parent, final Integer id) {
		super();
		this.dirName = path;
		this.id = id;
		this.parent = parent;
	}
	
	public String getDirName() {
		return dirName;
	}

	public int getId() {
		return id;
	}

	
	public void setParent(Folder parent) {
		this.parent = parent;
	}

	public Folder getParent() {
		return parent;
	}

}

Thank you very much for your help.

There’s at least one obvious problem:
This method always returns 1, as getChildFolderCount always returns 1:

	@Override
			public int getChildCount(HierarchicalQuery<Folder, Void> query) {
					return OrdnerkennungDAO.getChildFolderCount(query.getParent());

			}

This is not in sync with

			@Override
			protected Stream<Folder> fetchChildrenFromBackEnd(HierarchicalQuery<Folder, Void> query) {
						
					return  OrdnerkennungDAO.getChildFolder(query.getParent()).stream();
			}

which will always return a Stream with 10 entries. This will break things, as the count must be accurate for the component to calculate its scroll height correctly.

Thanks for the fast reply.

Did change the code:

public static int getChildFolderCount(Folder parent) {
		return getChildFolder(parent).size();
	}

	/*
	 * Just go on
	 */
	public static boolean hasChildFolder(Folder item) {
		return getChildFolderCount(item) > 0 ;
	}

	/*
	 * Adds 9 Folders into the List
	 */
	public static List<Folder> getChildFolder(Folder parent) {
		final LinkedList<Folder> folderList = new LinkedList<Folder>();
		for(int i=0;i<10;i++) {
			final Folder f = new Folder("dir" +i, parent, i);
			folderList.add(f);
		}
		return folderList;
	}

The problem still occurs, but deeper in hierarchy :frowning:

Okay, I think I finally figured out. The issue can be solved at least in the case of your code example by adding valid hashcode and equals methods to the Folder class.

-Olli

Hi Olli,

Thank you very much.
I did check the .equals and added a hashcode to the Folder class.
Works perfectly now :smiley: :smiley:

-Markus