Reputation: 47
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
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)));
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:
Upvotes: 3