Joseph Gagnon
Joseph Gagnon

Reputation: 2115

Vaadin 14 TreeGrid - Grid not loading hierarchy

I am writing an application which will display information logically represented as a collection of hierarchies of items. There will be a "top-level" list that will display the names of all the root nodes in the collection. When one of the items is selected, another list (the tree grid) will be loaded with the selected root node and I need it to display and provide access to everything in its hierarchy.

I can't figure out how to get the hierarchy to load in the grid.

I have defined a data provider class that extends AbstractBackEndHierarchicalDataProvider. It is my impression that the purpose of this class is to facilitate retrieving data in a hierarchical relationship. I also have a @Service class that is called from the provider to do the work. The class (TaskMaster) that represents the nodes does not contain any references to a "parent" or "children".

Here is the code:

TaskTreeView.java

public class TaskTreeView extends VerticalLayout {
  private TreeGrid<TaskMaster>   taskGrid;
  private TaskMasterDataProvider provider;
  private TaskMasterService      taskService;

  public TaskTreeView(TaskMasterService taskService) {
    this.taskService = taskService;
  }

  // Load the selected task (and its hierarchy).
  public void loadTasks(TopLevelTaskView.TaskSelectionEvent event) {
    if (event.getSelected() != null) {
      updateTree(event.getSelected());
    }
  }

  private void updateTree(TaskMaster task) {
    taskGrid.setItems(task);
    provider.refreshItem(task, true);
  }

  private void configureView() {
    taskGrid = new TreeGrid<>(TaskMaster.class);
    taskGrid.setColumns("name", "type");
    taskGrid.setHierarchyColumn("name");
    taskGrid.getColumns().forEach(col -> {
      col.setAutoWidth(true);
      col.setResizable(true);
    });

    provider = new TaskMasterDataProvider(taskService);
    taskGrid.setDataProvider(provider);
    add(taskGrid);
  }
}

TaskMasterDataProvider.java

public class TaskMasterDataProvider
    extends AbstractBackEndHierarchicalDataProvider<TaskMaster, Void> {
  private TaskMasterService service;

  public TaskMasterDataProvider(TaskMasterService service) {
    this.service = service;
  }

  @Override
  public int getChildCount(HierarchicalQuery<TaskMaster, Void> query) {
    return service.getChildCount(query.getParent());
  }

  @Override
  public boolean hasChildren(TaskMaster item) {
    return service.hasChildren(item);
  }

  @Override
  protected Stream<TaskMaster> fetchChildrenFromBackEnd(HierarchicalQuery<TaskMaster, Void> query) {
    return service.getChildren(query.getParent()).stream();
  }
}

TaskMasterService.java

@Service
@Transactional
public class TaskMasterService {
  private final TaskMasterRepository    taskMasterRepo;
  private final TaskRelationshipService taskRelationService;

  @Autowired
  public TaskMasterService(TaskMasterRepository taskMasterRepo,
      TaskRelationshipService taskRelationService) {
    this.taskMasterRepo = taskMasterRepo;
    this.taskRelationService = taskRelationService;
  }

  ...
  
  public int getChildCount(TaskMaster parent) {
    return parent != null
        ? taskRelationService.getChildRelationsForParent(parent.getInternalTaskID()).size()
        : 0;
  }

  public boolean hasChildren(TaskMaster parent) {
    return parent != null
        ? !taskRelationService.getChildRelationsForParent(parent.getInternalTaskID()).isEmpty()
        : false;
  }

  public List<TaskMaster> getChildren(TaskMaster parent) {
    List<TaskMaster> result = new ArrayList<>();

    if (parent != null) {
      List<TaskRelationship> childRelations = taskRelationService
          .getChildRelationsForParent(parent.getInternalTaskID());

      childRelations.forEach(relation -> {
        result.add(taskMasterRepo.findByInternalTaskID(relation.getChildTaskID()));
      });
    }

    return result;
  }
}

When I run the application and select an item from the "top level" list, the tree loads the root node and nothing else. Am I using the provider in the wrong way?

Domain data explanation (as best as I understand it):

TaskMaster

Contains information about "tasks", which is a broad description for a number of things, but is mainly used to define an application menu structure. The task type is one of the following: TASK_VIEW, INTERACTIVE, BATCH, FOLDER, URL, USER_DEFINED. The unique ID is internalTaskID. Note that this is NOT the database key. There is no concept of a "parent" or "children" in this data structure.

TaskRelationship

Contains a parent/child relationship between two "tasks" (i.e. TaskMaster entities).

Example:

In TaskMaster:

{
    internalTaskID: "1014",
    name: "Manufacturing Tasks",
    type: "00", // TASK_VIEW
}

{
    internalTaskID: "464E4534",
    name: "Manufacturing Inquiries",
    type: "07", // FOLDER
}

{
    internalTaskID: "4E3CBF33",
    name: "Item Ledger",
    type: "01", // INTERACTIVE
}

In TaskRelationship:

{
    parentTaskID: "1014",
    childTaskID: "464E4534",
}

{
    parentTaskID: "464E4534",
    childTaskID: "4E3CBF33",
}

Upvotes: 1

Views: 831

Answers (1)

Simon Martinelli
Simon Martinelli

Reputation: 36103

Your problem are these methods:

  public int getChildCount(TaskMaster parent) {
    return parent != null
        ? taskRelationService.getChildRelationsForParent(parent.getInternalTaskID()).size()
        : 0;
  }

  public boolean hasChildren(TaskMaster parent) {
    return parent != null
        ? !taskRelationService.getChildRelationsForParent(parent.getInternalTaskID()).isEmpty()
        : false;
  }

If you are on the root element of your tree the parent is always null. In that case you don't have to return false or 0.

In the case you are on the root element that has not parent you have to return true in the method hasChildren and the number of children of the root element in getChildCount

Upvotes: 1

Related Questions