在 Vaadin 8.1 TreeGrid 中替换 collapseItemsRecursively 和 expandItemsRecursively

Replacement for collapseItemsRecursively and expandItemsRecursively in Vaadin 8.1 TreeGrid

Vaadin 8.1 引入了 TreeGrid 组件。它不再有 collapseItemsRecursivelyexpandItemsRecursively 方法(在现在的遗留 Tree 组件中可用)。我错过了什么或者你需要开发自己的实现吗?如果是这样,推荐的方法是什么?

来自 docs for treegrid, you can use the methods, collapse and expand,通过传递树状网格数据项的列表或数组来展开或折叠:

treeGrid.expand(someTreeGridItem1, someTreeGridItem2);
treeGrid.collapse(someTreeGridItem1);

同样值得注意的是,显示 prevent certain items from ever being collapsed

能力的部分

我相信您已经注意到,TreeGrid 是一个相当新的组件,目前正在开发中,从 v8 开始可用。1.alphaX(当前的稳定版本是 v8.0.6)。因此,它目前可能只有一些基本功能,其余的将在未来的某个时候推出,尽管不能保证。例如 this similar feature request for the older TreeTable component 自 2011 年以来一直处于开放状态。

无论哪种方式,即使它们可能不是最佳解决方案,您也可以使用一些解决方法来实现此行为。我 无耻地 vaadin-sampler for TreeGrid.

当前可用代码的略微修改版本用作基本示例
public class RecursiveExpansionTreeGrid extends VerticalLayout {

    private Random random = new Random();

    public RecursiveExpansionTreeGrid() {
        // common setup with some dummy data
        TreeGrid<Project> treeGrid = new TreeGrid<>();
        treeGrid.setItems(generateProjectsForYears(2010, 2016), Project::getSubProjects);
        treeGrid.addColumn(Project::getName).setCaption("Project Name").setId("name-column");
        treeGrid.addColumn(Project::getHoursDone).setCaption("Hours Done");
        treeGrid.addColumn(Project::getLastModified).setCaption("Last Modified");
        addComponent(treeGrid);
    }

    // generate some dummy data to display in the tree grid
    private List<Project> generateProjectsForYears(int startYear, int endYear) {
        List<Project> projects = new ArrayList<>();

        for (int year = startYear; year <= endYear; year++) {
            Project yearProject = new Project("Year " + year);

            for (int i = 1; i < 2 + random.nextInt(5); i++) {
                Project customerProject = new Project("Customer Project " + i);
                customerProject.setSubProjects(Arrays.asList(
                        new LeafProject("Implementation", random.nextInt(100), year),
                        new LeafProject("Planning", random.nextInt(10), year),
                        new LeafProject("Prototyping", random.nextInt(20), year)));
                yearProject.addSubProject(customerProject);
            }
            projects.add(yearProject);
        }
        return projects;
    }

    // POJO for easy binding
    public class Project {
        private List<Project> subProjects = new ArrayList<>();
        private String name;

        public Project(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public List<Project> getSubProjects() {
            return subProjects;
        }

        public void setSubProjects(List<Project> subProjects) {
            this.subProjects = subProjects;
        }

        public void addSubProject(Project subProject) {
            subProjects.add(subProject);
        }

        public int getHoursDone() {
            return getSubProjects().stream().map(project -> project.getHoursDone()).reduce(0, Integer::sum);
        }

        public Date getLastModified() {
            return getSubProjects().stream().map(project -> project.getLastModified()).max(Date::compareTo).orElse(null);
        }
    }

    // Second POJO for easy binding
    public class LeafProject extends Project {
        private int hoursDone;
        private Date lastModified;

        public LeafProject(String name, int hoursDone, int year) {
            super(name);
            this.hoursDone = hoursDone;
            lastModified = new Date(year - 1900, random.nextInt(12), random.nextInt(10));
        }

        @Override
        public int getHoursDone() {
            return hoursDone;
        }

        @Override
        public Date getLastModified() {
            return lastModified;
        }
    }
}

接下来,递归展开或折叠节点有点取决于您的场景,但基本上它分解为同一件事:确保从根到最深叶的每个节点都是 expanded/collapsed。最简单的这样做的方法是 将您的层次结构展平 到节点列表中,然后调用适当的方法 expand(List<T> items)expand(T ... items) (第二个代表第一个和可能是一种方便的方法,例如 expand(myItem)).

为简单起见,我在 Project 实现中添加了一个 flatten 方法。如果由于某种原因您不能这样做,则创建一个递归方法,该方法创建一个列表,该列表从所选节点开始并包括所有子节点,子节点的,子节点的……好吧,您明白了。

public Stream<Project> flatten() {
    return Stream.concat(Stream.of(this), getSubProjects().stream().flatMap(Project::flatten));
}

可能的场景:

  1. 展开根时自动展开整个层级 - 添加监听器,expand/collapse整个展平层级:
treeGrid.addCollapseListener(event -> {
    if (event.isUserOriginated()) {
        // event is triggered by all collapse calls, so only do it the first time, when the user clicks in the UI
        // and ignore the programmatic calls
        treeGrid.collapse(event.getCollapsedItem().flatten().collect(Collectors.toList()));
    }
});
treeGrid.addExpandListener(event -> {
    if (event.isUserOriginated()) {
        // event is triggered by all expand calls, so only do it the first time, when the user clicks in the UI
        // and ignore the programmatic calls
        treeGrid.expand(event.getExpandedItem().flatten().collect(Collectors.toList()));
    }
});
  1. 使用上下文菜单等自定义操作扩展层次结构或其中的一部分
GridContextMenu<Project> contextMenu = new GridContextMenu<>(treeGrid);
contextMenu.addGridBodyContextMenuListener(contextEvent -> {
    contextMenu.removeItems();
    if (contextEvent.getItem() != null) {
        Project project = (Project) contextEvent.getItem();
        // update selection
        treeGrid.select(project);

        // show option for expanding
        contextMenu.addItem("Expand all", VaadinIcons.PLUS, event -> treeGrid.expand((project).flatten().collect(Collectors.toList())));

        // show option for collapsing
        contextMenu.addItem("Collapse all", VaadinIcons.MINUS, event -> treeGrid.collapse((project).flatten().collect(Collectors.toList())));
    }
});

最后你应该得到这样的效果: