Фильтрация столбцов сетки дерева Vaadin 8.1 на основе столбцов

Мне нужно отфильтровать сетку дерева на основе столбца. Я следовал решению, указанному в этот вопрос. Я попробовал приведенный ниже фрагмент кода для фильтрации, однако он не отображает никаких данных.

TreeDataProvider<Project> dataProvider = new TreeDataProvider<>(treeGrid.getTreeData());
dataProvider.setFilter(Project -> (Project.getStatus() != null && Project.getStatus().equals(Project.Status.PASS)));
treeGrid.setDataProvider(dataProvider);
treeGrid.getDataProvider().refreshAll();

Есть ли альтернативный способ фильтрации данных с помощью древовидных сеток vaadin 8.1.


person Misha    schedule 03.08.2017    source источник
comment
Вы отлаживали свою лямбду? Это должно дать вам понимание. Может быть, ни один корневой элемент не соответствует вашим критериям?   -  person Steffen Harbich    schedule 03.08.2017


Ответы (1)


Ваша проблема скорее всего

dataProvider.setFilter(Project -> (Project.getStatus() != null && Project.getStatus().equals(Project.Status.PASS)));

Я предполагаю, что ваши корневые и промежуточные узлы имеют нулевой статус, поэтому они не соответствуют фильтру, и в результате их дочерние узлы не будут отображаться.


Для простоты вы можете разрешить проекты со статусом null или соответствующие фильтру, но иногда вы можете получить пустые промежуточные узлы, потому что ни один дочерний элемент не будет соответствовать фильтру:

dataProvider.setFilter(Project -> (Project.getStatus() == null || Project.getStatus().equals(Project.Status.PASS)));

пустые узлы сетки дерева при фильтрации


Надежное решение немного сложнее, потому что вам нужно разрешить узлы, у которых есть хотя бы один дочерний элемент в иерархии, соответствующий фильтру. Это может быть излишним, но это самый быстрый способ использования данных в памяти, поэтому любой, у кого есть предложения по улучшению, поделитесь. Если бы данные были отфильтрованы из запроса к БД, вам, вероятно, это не понадобилось бы.

Во всяком случае, я добавил метод flatten к Project, который, очевидно, сглаживает иерархию проекта в поток элементов, которые вы можете быстро просмотреть и посмотреть, соответствует ли какой-либо из них вашему фильтру. Ниже приведена полная реализация, основанная на другой вопрос, который вы связали. Я также изменил способ первоначальной загрузки данных в древовидную сетку, поэтому вам не нужно делать хаки, такие как new TreeDataProvider<>(treeGrid.getTreeData());

import com.vaadin.data.TreeData;
import com.vaadin.data.provider.TreeDataProvider;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.Notification;
import com.vaadin.ui.TreeGrid;
import com.vaadin.ui.VerticalLayout;

import java.util.*;
import java.util.stream.Stream;

public class BasicTreeGrid extends VerticalLayout {

    // used to generate some random data
    private final Random random = new Random();

    public BasicTreeGrid() {
        // basic setup
        setSizeFull();
        TreeGrid<Project> treeGrid = new TreeGrid<>();
        treeGrid.setSizeFull();
        addComponent(treeGrid);
        treeGrid.addColumn(Project::getName).setCaption("Project Name").setId("name-column");
        treeGrid.addColumn(Project::getHoursDone).setCaption("Hours Done").setId("hour-column");
        treeGrid.addColumn(Project::getLastModified).setCaption("Last Modified").setId("date-column");
        treeGrid.addColumn(Project::getStatus).setCaption("Status").setId("status-column");

        // some listeners for interaction
        treeGrid.addCollapseListener(event -> Notification
                .show("Project '" + event.getCollapsedItem().getName() + "' collapsed.", Notification.Type.TRAY_NOTIFICATION));
        treeGrid.addExpandListener(event -> Notification
                .show("Project '" + event.getExpandedItem().getName() + "' expanded.", Notification.Type.TRAY_NOTIFICATION));


        // add the list of root projects and specify a provider of sub-projects
        TreeData<Project> data = new TreeData<>();
        data.addItems(generateProjectsForYears(2010, 2016), Project::getSubProjects);
        TreeDataProvider<Project> dataProvider = new TreeDataProvider<>(data);
        treeGrid.setDataProvider(dataProvider);

        // filter combo setup
        ComboBox<Status> statusFilter = new ComboBox<>();
        statusFilter.setItems(Status.values());
        statusFilter.setEmptySelectionCaption("ALL");
        statusFilter.addValueChangeListener(event -> {
            if (event.getValue() == null) {
                dataProvider.clearFilters();
            } else {
                dataProvider.setFilter(project -> {
                    if (project.getSubProjects() == null | project.getSubProjects().isEmpty()) {
                        // include final nodes matching the filter
                        return project.getStatus() == null || project.getStatus() == event.getValue();
                    } else {
                        // include root and intermediate nodes that have children matching the filter
                        return project.flatten().anyMatch(subProject -> subProject.getStatus() == event.getValue());
                    }
                });
            }
        });

        // add filter combo to header
        treeGrid.appendHeaderRow().getCell("status-column").setComponent(statusFilter);
    }

    // generate some random projects
    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, Status.values()[random.nextInt(3)]),
                        new LeafProject("Planning", random.nextInt(10), year, Status.values()[random.nextInt(3)]),
                        new LeafProject("Prototyping", random.nextInt(20), year, Status.values()[random.nextInt(3)])));
                yearProject.addSubProject(customerProject);
            }
            projects.add(yearProject);
        }
        return projects;
    }

    // project status
    enum Status {
        NOT_STARTED, IN_PROGRESS, DONE
    }

    // basic parent (or intermediate child) bean used for easy binding
    class Project {
        private List<Project> subProjects = new ArrayList<>();
        private String name;
        private Status status;

        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);
        }

        public Status getStatus() {
            return status;
        }

        public void setStatus(Status status) {
            this.status = status;
        }

        // flatten the project hierarchy into a stream of items
        public Stream<Project> flatten() {
            return Stream.concat(
                    Stream.of(this),
                    subProjects.stream().flatMap(Project::flatten));
        }
    }


    // basic final child (can not have other children) bean used for easy binding
    class LeafProject extends Project {
        private int hoursDone;
        private Date lastModified;

        public LeafProject(String name, int hoursDone, int year, Status status) {
            super(name);
            setStatus(status);
            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;
        }
    }
}

Результат:

фильтрация сетки дерева

person Morfic    schedule 03.08.2017
comment
Проблема заключается в том, что при фильтрации TreeDataProvider с помощью SerializablePredicate, когда родительский объект не может быть предварительно просмотрен, все его дочерние элементы также становятся скрытыми. Упомянутая здесь идея выравнивания помогла мне. У меня есть список строк, и теперь мне нужно сохранить ссылку на дочерние элементы в родительских строках. Затем, когда происходит фильтрация, если родительская строка не подходит, я просматриваю список дочерних элементов, и как только один дочерний элемент подходит, родительская строка становится подходящей. Таким образом, родительская строка не помечается как скрытая, и это оставляет возможность для дочерней строки также быть оцененной на предмет видимости. - person Youness; 26.04.2019