Important Notice - Forums is archived
To simplify things and help our users to be more productive, we have archived the current forum and focus our efforts on helping developers on Stack Overflow. You can post new questions on Stack Overflow or join our Discord channel.

Vaadin lets you build secure, UX-first PWAs entirely in Java.
Free ebook & tutorial.
Filtering a TreeTable
I have a TreeTable class that I need to be able to filter via the contents of a TextField. (I can worry about styling said TextField later so it has a magnifying glass icon and an "X" button for quickling clearing it.)
I've attempted to follow along with the Vaadin Book example, modifying it slightly so it should work better in my use case. For example, I keep them all in one object, since the methods "changeDayTo" and "filterTable" seem like they should be internal to this TreeTable. (Keeping in mind that whole 'objects should only contain properties and methods that are specific to themselves' idea.)
So here's my semi-custom TreeTable class:
/**
*
*/
package info.chrismcgee.sky.scheduling.ui.components;
import java.sql.SQLException;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.vaadin.data.Container.Filterable;
import com.vaadin.data.util.filter.SimpleStringFilter;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
import com.vaadin.ui.Notification;
import com.vaadin.ui.TreeTable;
import com.vaadin.ui.Notification.Type;
import info.chrismcgee.sky.scheduling.beans.Job;
import info.chrismcgee.sky.scheduling.beans.OrderDetail;
import info.chrismcgee.sky.tables.JobManager;
/**
* @author Marketing
*
*/
public class ProductionTreeTable extends TreeTable {
/**
* Serialization!
*/
private static final long serialVersionUID = -8456241351283529486L;
private final static Logger logger = Logger.getLogger(ProductionTreeTable.class.getName()); // Logging!
private SimpleStringFilter filter = null;
private Filterable f = null;
/**
* Constructor
*/
public ProductionTreeTable() {
super();
// Set the columns.
addContainerProperty("Name / Product", HorizontalLayout.class, "");
addContainerProperty("Job # / Detail", String.class, "");
addContainerProperty("Print Type", String.class, "");
addContainerProperty("Colors", Long.class, 0L);
addContainerProperty("Quantity", Long.class, 0L);
addContainerProperty("Total", Long.class, 0L);
}
HorizontalLayout createNode (String name, boolean isChecked) {
HorizontalLayout layout = new HorizontalLayout();
layout.addComponent(new CheckBox(null, isChecked));
layout.addComponent(new Label(name));
return layout;
}
public void changeDayTo (final Date date) {
// Get a list of jobs on a particular date.
filter = null; // Reset the filter.
List<Job> jobsList = null;
try {
jobsList = JobManager.getJobsByDate(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (jobsList != null && jobsList.size() > 0) {
removeAllItems(); // Clear out the TreeTable first.
for (final Job j : jobsList) {
// Go through each item in the Jobs list and add it
// (along with all of its OrderDetail items) to the TreeTable.
final Object jobId = addItem(new Object[] {createNode(j.getCustomerName(), j.getJobCompleted() != null), j.getJobId(),
"", null, null, null}, null);
for (final OrderDetail od : j.getOrderDetailList()) {
final Object odId = addItem(new Object[] {createNode(od.getProductId(), od.getItemCompleted() != null), od.getProductDetail(),
od.getPrintType().getValue(), od.getNumColors(), od.getQuantity(), (od.getNumColors() * od.getQuantity())}, null);
boolean parentSet = setParent(odId, jobId);
setChildrenAllowed(odId, false);
setCollapsed(odId, false);
}
setCollapsed(jobId, false);
}
filterTable("");
}
}
public void filterTable (String searchText) {
// TODO Auto-generated method stub
f = (Filterable) getContainerDataSource();
// Remove old filter
if (filter != null) {
f.removeContainerFilter(filter);
}
// Set new filter for the "Name / Product" column
filter = new SimpleStringFilter("Name / Product", searchText, true, false);
f.addContainerFilter(filter);
Notification.show("Results narrowed.", Type.TRAY_NOTIFICATION);
}
}
Unfortunately, anything typed in the text field (which gets passed to the filterTable() method) causes the TreeTable to be completely emptied. I'm sure it has something to do with the Filterable property and/or the SimpleStringFilter object I'm trying to use here, but I don't know what it is. I'm completely new to filtering. All I do know is that, once the TreeTable has been populated with data in the changeDayTo() method, it is using a HierarchicalContainer, which *does* implement the Container.Filterable interface, from what I understand. Now I just need to know how to take advantage of this.
Please help?
Hi,
Could it be related to some parent-filter behavior ?
https://vaadin.com/api/7.6.0/com/vaadin/data/util/HierarchicalContainer.html#setIncludeParentsWhenFiltering%28boolean%29
Anyway, not answering the question, but you can also take a look at one of the best add-on : https://vaadin.com/directory#!addon/filteringtable, that will do most of the job for you ;-)
Regards
Sebastien
Hi, Sebastien.
I suppose it's possible that might have something to do with the problem, but here's something else interesting that may be relevant: In my `ProductionTreeTable` class, whenever I attempt to do a `getContainerDataSource()`, I am not given the option to `.setIncludeParentsWhenFiltering` on it. Eclipse does not give me that option, meaning that `getContainerDataSource()` doesn't actually return a HierarchicalContainer (nor something that extends it), despite the fact that the tooltip I get on `getContainerDataSource()` specifically says that it returns "Hierarchical." Now I'm even more confused.
Also, I looked at that add-on, but I'm not so sure that I really want to try it. I have enough trouble just getting Vaadin's stock stuff to work, even with all of the documentation surrounding them; I'd be afraid that I might be out of my league attempting to use a community-made add-on with very little documentation (which is probably geared for someone who knows much more about Java and Vaadin than I).
// Set the columns. addContainerProperty("Name / Product", HorizontalLayout.class, "");
That looked a bit strange.. I assume HorizontalLayout.class should be String.class?
I see this is the column you filter on, so it might be relevant.
Guttorm Vik:
// Set the columns. addContainerProperty("Name / Product", HorizontalLayout.class, "");
That looked a bit strange.. I assume HorizontalLayout.class should be String.class?
I see this is the column you filter on, so it might be relevant.
I believe you've hit upon it, Guttorm. Actually, I do want that column to be a HorizontalLayout, since--depending on the user's access level--there may be either just a Label in that column or both a Checkbox *and* a Label.
The trick seems to be in getting at that Label part. I'm guessing the SimpleStringFilter assumes that the column must be of the String.class, so perhaps I have to use a not-so-simple filter? Either way, how would I direct the filter to dig into that HorizontalLayout to get at the Label inside it?
Okay, so I had a bit of inspiration this afternoon. Since I couldn't figure out any way of changing the fact that SimpleStringFilter only works on a String, I decided to work around this limitation. Reading the API doc for SimpleStringFilter showed me that "values of other types are converted using toString()." How could I get the HorizontalLayout to return a String?
I created a new class that extends HorizontalLayout and overrides .toString() to return the Label's value!
package info.chrismcgee.sky.scheduling.ui.components;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Label;
public class TreeNode extends HorizontalLayout {
/**
* Serialization!
*/
private static final long serialVersionUID = 232800657274822961L;
private CheckBox checkbox;
private Label label;
/**
* @param checkbox
* @param label
*/
public TreeNode(String name, boolean isChecked) {
super();
checkbox = new CheckBox(null, isChecked);
label = new Label(name);
addComponent(checkbox);
addComponent(label);
}
@Override
public String toString() {
// TODO Auto-generated method stub
return label.getValue();
}
}
Seems so simple, now. Of course, my code for it is probably über-sloppy, but at least it works now!
Now I just need to figure out how to have it filter more properly. i.e., to include the parent/child node when filtering. Also to have it not show the checkbox if the logged-in user doesn't have sufficient privileges.