You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

datamodel-hierarchical.asciidoc 6.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. ---
  2. title: Hierarchical Data
  3. order: 6
  4. layout: page
  5. ---
  6. [[datamodel.hierarchical]]
  7. = Hierarchical Data
  8. The [classname]#Tree# and the [classname]#TreeGrid# components allow you to show data with hierarchical relationships between items.
  9. That data can be populated by on-demand from a back end by implementing the [interfacename]#HierarchicalDataProvider# interface. If you have the data available in-memory on the server,
  10. you use the collection style API of [classname]#TreeData# and then pass it to a [classname]#TreeDataProvider#. This chapter introduces the hierarchical data providers and how they work.
  11. For using them with the components you should see <<../components/components-tree.asciidoc#components.tree,"Tree">>
  12. and <<../components/components-treegrid.asciidoc#components.treegrid,"TreeGrid">> documentation.
  13. == In-memory Hierarchical Data
  14. When the hierarchical data is available in the server side memory, you can use it to populate the [classname]#TreeData# that is the source of data for an [classname]#TreeDataProvider#. It contains collection style API to construct the hierarchical structure of your data, and also verifies that the hierarchy structure is valid.
  15. The following example populates a [classname]#TreeData# with two levels of data:
  16. [source, java]
  17. ----
  18. Collection<Project> projects = service.getProjects();
  19. TreeData<Project> data = new TreeData<>();
  20. // add root level items
  21. data.addItems(null, projects);
  22. // add children for the root level items
  23. projects.forEach(project -> data.addItems(project, project.getChildren()));
  24. // construct the data provider for the hierarchical data we've built
  25. TreeDataProvider<Project> dataProvider = new TreeDataProvider<>(data);
  26. ----
  27. === Updating data
  28. When adding or removing items from the [classname]#TreeData#, you need to always notify the data provider that it should refresh its data. This can be done with the [methodname]#refreshAll# method in the data provider.
  29. [source, java]
  30. ----
  31. TreeData<Project> data = dataProvider.getTreeData();
  32. data.addItem(null, newProject);
  33. data.addItems(newProject, newProject.getChildren());
  34. // removes the item and all of its children
  35. data.removeItem(oldProject);
  36. // refresh data provider and the components it is bound to
  37. dataProvider.refreshAll();
  38. ----
  39. === Sorting and Filtering
  40. For [classname]#TreeDataProvider#, 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.
  41. [source, java]
  42. ----
  43. // setting sorting or filtering automatically refreshes the data
  44. dataProvider.setSortComparator((projectA, projectB) ->
  45. projectA.getHours().compareTo(projectB.getHours()));
  46. dataProvider.setFilter(project -> project.getHours() > 100);
  47. ----
  48. == Lazy Loading Hierarchical Data from a Back End
  49. The lazy loading hierarchical data, same concepts apply as with the non-hierarchical data, so you should take a look at <<datamodel-providers.asciidoc#datamodel.dataproviders.lazy,"Lazy Loading Data to a Listing">> if you have not already.
  50. To load hierarchical data on-demand from your back end, you should extend the [classname]#AbstractHierarchicalDataProvider# class. Then you just have to implement the following three methods:
  51. * `boolean hasChildren(T item)`
  52. ** 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.
  53. * `int getChildCount(HierarchicalQuery<T, F> query)`
  54. ** This method returns the number of children for a certain tree level, but only for that level, excluding all subtrees
  55. ** The parent node is available in the [classname]#HierarchicalQuery# via the [methodname]#getParent# method, which returns `null` for the root level.
  56. ** This method is only called when a node has been expanded
  57. * `Stream<T> fetchChildren(HierarchicalQuery<T, F> query)`
  58. ** This method returns a subset of the children for a certain tree level
  59. ** The subset starts from the index retuned by the [methodname]#getOffset# method, thus for fetching the first item for a subtree it is always 0
  60. ** The amount of nodes to fetch is returned by the [methodname]#getLimit# method, thus the amount of nodes returned should always (!) be the same as the _limit_.
  61. ** The parent node is available in the [classname]#HierarchicalQuery# via the [methodname]#getParent# method, which returns `null` for the root level.
  62. ** This method is called whenever the data should be displayed in the UI
  63. Note that the [classname]#HierarchicalQuery# query object contains the relevant information regarding the sorting and filtering.
  64. The following code snippet shows a simple example on how to building a lazy hierarchical data provider based on file system structure:
  65. [source, java]
  66. ----
  67. class FileSystemDataProvider extends
  68. AbstractBackEndHierarchicalDataProvider<File, FilenameFilter> {
  69. private final File root;
  70. public FileSystemDataProvider(File root) {
  71. this.root = root;
  72. }
  73. @Override
  74. public int getChildCount(
  75. HierarchicalQuery<File, FilenameFilter> query) {
  76. return (int) fetchChildren(query).count();
  77. }
  78. @Override
  79. public Stream<File> fetchChildrenFromBackEnd(
  80. HierarchicalQuery<File, FilenameFilter> query) {
  81. final File parent = query.getParentOptional().orElse(root);
  82. return query.getFilter()
  83. .map(filter -> Stream.of(parent.listFiles(filter)))
  84. .orElse(Stream.of(parent.listFiles()))
  85. .skip(query.getOffset()).limit(query.getLimit());
  86. }
  87. @Override
  88. public boolean hasChildren(File item) {
  89. return item.list() != null && item.list().length > 0;
  90. }
  91. }
  92. ----
  93. If there are any updates on the hierarchical data, such as adding or removing rows, you should call the [methodname]#refreshAll# method that is inherited by extending [classname]#AbstractHierarchicalDataProvider#. This will reset the data. If only the data for a specific item has been updated, you can call the [methodname]#refreshItem# method to only update that item.