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.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. ---
  2. title: Hierarchical Data
  3. order: 6
  4. layout: page
  5. ---
  6. [[datamodel.hierarchical]]
  7. = Hierarchical Data
  8. IMPORTANT: The [interfacename]#HierarchicalDataProvider# is currently being developed and only available in the Framework 8.1 prerelease versions, starting from 8.1.0.alpha1.
  9. The [classname]#Tree# and the [classname]#TreeGrid# components allow you to show data with hierarchical relationships between items.
  10. 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,
  11. 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.
  12. For using them with the components you should see <<dummy/../../../framework/components/components-tree.asciidoc#components.tree,"Tree">>
  13. and <<dummy/../../../framework/components/components-treegrid.asciidoc#components.treegrid,"TreeGrid">> documentation.
  14. == In-memory Hierarchical Data
  15. 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.
  16. The following example populates a [classname]#TreeData# with two levels of data:
  17. [source, java]
  18. ----
  19. Collection<Project> projects = service.getProjects();
  20. TreeData<Project> data = new TreeData<>();
  21. // add root level items
  22. data.addItems(null, projects);
  23. // add children for the root level items
  24. projects.forEach(project -> data.addItems(project, project.getChildren()));
  25. // construct the data provider for the hierarchical data we've built
  26. TreeDataProvider<Project> dataProvider = new TreeDataProvider<>(data);
  27. ----
  28. === Updating data
  29. 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.
  30. [source, java]
  31. ----
  32. TreeData<Project> data = dataProvider.getTreeData();
  33. data.addItem(null, newProject);
  34. data.addItems(newProject, newProject.getChildren());
  35. // removes the item and all of its children
  36. data.removeItem(oldProject);
  37. // refresh data provider and the components it is bound to
  38. dataProvider.refreshAll();
  39. ----
  40. === Sorting and Filtering
  41. 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.
  42. [source, java]
  43. ----
  44. // setting sorting or filtering automatically refreshes the data
  45. dataProvider.setSortComparator((projectA, projectB) ->
  46. projectA.getHours().compareTo(projectB.getHours()));
  47. dataProvider.setFilter(project -> project.getHours() > 100);
  48. ----
  49. == Lazy Loading Hierarchical Data from a Back End
  50. The lazy loading hierarchical data, same concepts apply as with the non-hierarchical data, so you should take a look at <<dummy/../../../framework/datamodel/datamodel-providers.asciidoc#datamodel.dataproviders.lazy,"Lazy Loading Data to a Listing">> if you have not already.
  51. 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:
  52. * `boolean hasChildren(T item)`
  53. ** 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.
  54. * `int getChildCount(HierarchicalQuery<T, F> query)`
  55. ** This method returns the number of children for a certain tree level, but only for that level, excluding all subtrees
  56. ** The parent node is available in the [classname]#HierarchicalQuery# via the [methodname]#getParent# method, which returns `null` for the root level.
  57. ** This method is only called when a node has been expanded
  58. * `Stream<T> fetchChildren(HierarchicalQuery<T, F> query)`
  59. ** This method returns a subset of the children for a certain tree level
  60. ** 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
  61. ** 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_.
  62. ** The parent node is available in the [classname]#HierarchicalQuery# via the [methodname]#getParent# method, which returns `null` for the root level.
  63. ** This method is called whenever the data should be displayed in the UI
  64. Note that the [classname]#HierarchicalQuery# query object contains the relevant information regarding the sorting and filtering.
  65. The following code snippet shows a simple example on how to building a lazy hierarchical data provider based on file system structure:
  66. [source, java]
  67. ----
  68. class FileSystemDataProvider extends
  69. AbstractBackEndHierarchicalDataProvider<File, FilenameFilter> {
  70. private final File root;
  71. public FileSystemDataProvider(File root) {
  72. this.root = root;
  73. }
  74. @Override
  75. public int getChildCount(
  76. HierarchicalQuery<File, FilenameFilter> query) {
  77. return (int) fetchChildren(query).count();
  78. }
  79. @Override
  80. public Stream<File> fetchChildrenFromBackEnd(
  81. HierarchicalQuery<File, FilenameFilter> query) {
  82. final File parent = query.getParentOptional().orElse(root);
  83. return query.getFilter()
  84. .map(filter -> Stream.of(parent.listFiles(filter)))
  85. .orElse(Stream.of(parent.listFiles()))
  86. .skip(query.getOffset()).limit(query.getLimit());
  87. }
  88. @Override
  89. public boolean hasChildren(File item) {
  90. return item.list() != null && item.list().length > 0;
  91. }
  92. }
  93. ----
  94. 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.