Misha
Misha

Reputation: 47

Vaadin 8.1 tree grids column based filtering

I need to filter a tree grid based on a column. I've followed the solution stated in this question. I've tried the code snippet below for filtering however it does not display any data.

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

Is there an alternative way to filter data with vaadin 8.1 tree grids.

Upvotes: 1

Views: 1668

Answers (1)

Morfic
Morfic

Reputation: 15508

Your problem is most likely

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

I assume your root and intermediate nodes have a null status, so they don't match the filter, and as a result their children won't be displayed.


To make it simple, you could allow projects with status null, or matching the filter, but occasionally you may end up with empty intermediate nodes because no child will match the filter:

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

tree grid empty nodes on filtering


The bullet-proof solution is a bit trickier, because you need to allow nodes that have at least one child in the hierarchy that matches the filter. This might be overkill but it's the quickest way I could think of using in memory data, so anyone that has suggestions for improvements, please do share. If the data were filtered from a DB query you probably wouldn't be needing this.

Anyway, I've added a flatten method to Project which, obviously, flattens the project hierarchy into a stream of items that you can quickly traverse and see if any of them matches your filter. Bellow is the full implementation, based on the other question you linked. I've also changed how the data is initially fed into the tree grid, so you don't have to do hacks like 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;
        }
    }
}

Result:

tree grid filtering

Upvotes: 3

Related Questions