Hi,
I know that there is a super filter over there, but I think that it would be really great to have a filter like excel’s one.
This kind of filter you can find in SQL Developer as well…
What do you think ?
PS: The filter we have is more considerate as an advanced filter, if the user want to filter on a column, he/she need to click many times to get the result.
I think you could reach something very close by building a UI layer or the current filter mechanism. After all Excel filter is just a list of criteria over properties linked by AND logic. Current CUBA filter already permits that, it should be just a matter of presenting it over column headers like in Excel.
Hi Konatantin
I also need this option in my application. This will be a very good feature of Table / DataGrid components. Thanks for considering this feature.
Hi
I started building something like that for use with my datagrids, using functionality built-in cuba platform.
I agree it would be nice to have this built-in as a drag-n-drop component in Cuba Studio though!
The aim is to add filters to each column of the datagrid with as little work as possible, so I tried to build it as a generic utility that you can call on existing DataGrids to add filter. This is how to call it:
//Add filters to the DataGrid
DataGridFilter<Person> filter= new DataGridFilter<Person>(personsDataGrid);
filter.addGenericFilter();
And here is the code for the DataGridFilter class. Any comments or improvements are welcome!
import java.util.HashMap;
import java.util.List;
import com.google.common.base.Strings;
import com.haulmont.chile.core.datatypes.impl.EnumClass;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.filter.Clause;
import com.haulmont.cuba.core.global.filter.LogicalCondition;
import com.haulmont.cuba.core.global.filter.LogicalOp;
import com.haulmont.cuba.core.global.filter.QueryFilter;
import com.haulmont.cuba.gui.components.DataGrid;
import com.haulmont.cuba.gui.components.LookupField;
import com.haulmont.cuba.gui.components.TextField;
import com.haulmont.cuba.gui.components.DataGrid.Column;
import com.haulmont.cuba.gui.components.DataGrid.HeaderCell;
import com.haulmont.cuba.gui.components.DataGrid.HeaderRow;
import com.haulmont.cuba.gui.xml.layout.ComponentsFactory;
public class DataGridFilter<E extends Entity> {
private HeaderRow filterHeaderRow;
private HashMap<String,String> filterMap;
private String originalQueryString;
private DataGrid<E> targetDataGrid;
public DataGridFilter(DataGrid<E> datagrid) {
targetDataGrid = datagrid;
originalQueryString = targetDataGrid.getDatasource().getQuery();
filterHeaderRow = targetDataGrid.appendHeaderRow();
filterMap = new HashMap<String,String>();
}
public void addGenericFilter() {
ComponentsFactory componentsFactory = AppBeans.get(ComponentsFactory.class);
MetaClass metaClass = targetDataGrid.getDatasource().getMetaClass();
List<Column> colList = targetDataGrid.getColumns();
for (Column col : colList) {
String columnProperty = col.getId();
MetaProperty metaProp = metaClass.getProperty(columnProperty);
if (metaProp !=null) {
HeaderCell filterCell;
//For Datatype use a textfield and search against varchar cast of DB value
if (metaProp.getRange().isDatatype()) {
TextField textField = componentsFactory.createComponent(TextField.class);
textField.setId(columnProperty);
textField.setWidth("100%");
textField.setStyleName("tiny");
if (metaProp.isReadOnly()) {
textField.setEnabled(false);
}
textField.addValueChangeListener(listener->{
if(Strings.isNullOrEmpty(textField.getValue())) {
filterMap.remove(columnProperty);
}else {
filterMap.put(columnProperty, "%" + textField.getRawValue().toLowerCase()+"%");
}
});
textField.addEnterPressListener(e -> {
applyGridFilter();
});
filterCell = filterHeaderRow.getCell(columnProperty);
filterCell.setComponent(textField);
}else {
//For Enum use lookup field
if (metaProp.getRange().isEnum()) {
LookupField lookupField = componentsFactory.createComponent(LookupField.class);
lookupField.setId(columnProperty);
lookupField.setWidth("100%");
lookupField.setStyleName("tiny");
lookupField.setOptionsEnum((Class<? extends EnumClass>) (metaProp.getJavaType()));
if (metaProp.isReadOnly()) {
lookupField.setEnabled(false);
}
lookupField.addValueChangeListener(listener->{
if (lookupField.getValue() == null) {
filterMap.remove(columnProperty);
}else {
filterMap.put(columnProperty, EnumClass.class.cast(lookupField.getValue()).getId().toString().toLowerCase());
}
applyGridFilter();
});
filterCell = filterHeaderRow.getCell(columnProperty);
filterCell.setComponent(lookupField);
}
}
}
}
}
private void applyGridFilter() {
if (filterMap.size() == 0) {
resetQuery();
targetDataGrid.getDatasource().refresh();
} else {
targetDataGrid.getDatasource().setQueryFilter(null);
LogicalCondition andCondition = new LogicalCondition("", LogicalOp.AND);
for (String key : filterMap.keySet()) {
andCondition.getConditions().add(new Clause("", "lower(cast(e." + key +" varchar(100))) like :custom$" + key, null, null, null));
}
QueryFilter queryFilter = new QueryFilter(andCondition);
targetDataGrid.getDatasource().setQueryFilter(queryFilter);
targetDataGrid.getDatasource().refresh(filterMap);
}
}
private void resetQuery() {
targetDataGrid.getDatasource().setQueryFilter(null);
}
}
Ok, so my generic filter above is quite limited, it only works on direct Datatype and Enum attributes.
I would like to enhance it to work with 1) a related entity’s instance name, and 2) a related entity’s Datatype or Enum.
For 1) (Related entity), I see a solution using getNamePatternProperties() and building an OR query on each attribute used in the NamePattern.
For 2) (Attribute of related entity) however, I don’t know how I could go down the relation hierarchy down to the property shown in the column.
For example, if my column shows “customer.order.quantity”, do I need to split the metaproperty string by “.” (dot) and work with that to get the “quantity” metaproperty of the “order” metaclass? Or is there a better way?
Any help appreciated!
Thank you
EDIT: Ok I found what I was looking for: the “getPropertyPath” function solved my problem.
If anyone is interested please find the updated version linked below. Filtering properties of related entities is now supported. I also added a function to add custom queries, if you need to set something special for a specific column.