Filter like in Excel

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.

That’s a good idea. We thought about it, but didn’t do because it’s quite hard to implement.

Let’s wait and see if there will be votes for this feature.

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.

1 Like

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.

Mortoza

Hi Konstantin @knstvk
Is there is further update on this?

How can we have filter in DataGrid like Vaadin grid here? If it is like excel that will be even better.
image

1 Like

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! :blush:

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

}

Hope this will be useful to someone.
Regards,

3 Likes

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.

http://badaboum.bidibom.free.fr/DataGridFilter.java

Regards

Hi Konatantin
This feature request looks got a good level of votes, 455 views, 18 likee… Is this now on the roadmap?

Mortoza

Could this Vaadin add-on be used?

1 Like

You can use it, but only as an additional Component. Unfortunately, it cannot be integrated with Table component of the platform.

could be a great feature have filter direct on columns table

1 Like