You are viewing documentation for Vaadin Framework 8 and related products View documentation for Vaadin Framework 7 ›
Hierarchical Data · Vaadin
Vaadin Framework - Vaadin Data Model - Hierarchical Data
 Edit This Page

Hierarchical Data

The HierarchicalDataProvider is currently being developed and only available in the Framework 8.1 prerelease versions, starting from 8.1.0.alpha1.

The TreeGrid component allows you to show data with hierarchical relationships between items. That data can be populated by on-demand from a back end by implementing the HierarchicalDataProvider interface. If you have the data available in-memory on the server, you use the collection style API of HierarchyData and then pass it to a InMemoryHierarchicalDataProvider. This chapter introduces the hierarchical data providers and how they work. For using them with the TreeGrid component you should see its documentation.

In-memory Hierarchical Data

When the hierarchical data is available in the server side memory, you can use it to populate the HierarchyData that is the source of data for an InMemoryHierarchicalDataProvider. It contains collection style API to construct the hierarchical structure of your data, and also verifies that the hierarchy structure is valid.

The following example populates a HierarchyData with two levels of data:

Collection<Project> projects = service.getProjects();

HierarchyData<Project> data = new HierarchyData<>();
// add root level items
data.addItems(null, projects);

// add children for the root level items
projects.forEach(project -> data.addItems(project, project.getChildren());

// construct the data provider for the hierarchical data we've built
InMemoryHierarchicalDataProvider<Project> dataProvider = new InMemoryHierarchicalDataProvider<>(data);

Updating data

When adding or removing items from the HierarchyData, you need to always notify the data provider that it should refresh its data. This can be done with the refreshAll method in the data provider.

HierarchyData<Project> data = dataProvider.getHierarchyData();
data.addItem(null, newProject);
data.addItems(newProject, newProject.getChildren());

// removes the item and all of its children
data.removeItem(oldProject);

// refresh data provider and the components it is bound to
dataProvider.refreshAll();

Sorting and Filtering

For InMemoryHierarchicalDataProvider, both the sorting and filtering API is the same as in [classname]ListDataProvider#. Sorting and filtering are applied separately for each hierarchy level, meaning e.g. that for a node that has not passed the filter there are no children shown.

// setting sorting or filtering automatically refreshes the data
dataProvider.setSortComparator((projectA, projectB) ->
      projectA.getHours().compareTo(projectB.getHours()));

dataProvider.setFilter(project -> project.getHours() > 100);

Lazy Loading Hierarchical Data from a Back End

The lazy loading hierarchical data, same concepts apply as with the non-hierarchical data, so you should take a look at Lazy Loading Data to a Listing if you have not already.

To load hierarchical data on-demand from your back end, you should extend the AbstractHierarchicalDataProvider class. Then you just have to implement the following three methods:

  • boolean hasChildren(T item)

    • This tells the data provider whether the given item has children and should be expandable. Note that this method is called frequently and should not do any costly operations.

  • int getChildCount(HierarchicalQuery<T, F> query)

    • This method returns the number of children for a certain tree level, but only for that level, excluding all subtrees

    • The parent node is available in the HierarchicalQuery via the getParent method, which returns null for the root level.

    • This method is only called when a node has been expanded

  • Stream<T> fetchChildren(HierarchicalQuery<T, F> query)

    • This method returns a subset of the children for a certain tree level

    • The subset starts from the index retuned by the getOffset method, thus for fetching the first item for a subtree it is always 0

    • The amount of nodes to fetch is returned by the getLimit method, thus the amount of nodes returned should always (!) be the same as the limit.

    • The parent node is available in the HierarchicalQuery via the getParent method, which returns null for the root level.

    • This method is called whenever the data should be displayed in the UI

Note that the HierarchicalQuery query object contains the relevant information regarding the sorting and filtering.

The following code snippet shows a simple example on how to building a lazy hierarchical data provider based on file system structure:

class FileSystemDataProvider
      extends AbstractHierarchicalDataProvider<File, FilenameFilter> {
  private final File root;

  public FileSystemDataProvider(File root) {
    this.root = root;
  }

  @Override
  public int getChildCount(
      HierarchicalQuery<File, FilenameFilter> query) {
    return (int) fetchChildren(query).count();
  }

  @Override
  public Stream<File> fetchChildren(
      HierarchicalQuery<File, FilenameFilter> query) {
    final File parent = query.getParentOptional().orElse(root);
    return query.getFilter()
          .map(filter -> Stream.of(parent.listFiles(filter)))
          .orElse(Stream.of(parent.listFiles()))
          .skip(query.getOffset()).limit(query.getLimit());
  }

  @Override
  public boolean hasChildren(File item) {
    return item.list() != null && item.list().length > 0;
  }

  @Override
  public boolean isInMemory() {
    return false;
  }
}

If there are any updates on the hierarchical data, such as adding or removing rows, you should call the refreshAll method that is inherited by extending AbstractHierarchicalDataProvider. This will reset the data. If only the data for a specific item has been updated, you can call the refreshItem method to only update that item.