How are you expected to do a dynamic filter of a grid using a BackEndDataProvider? I have a fully functional instance, but I feel like the way I made it work cannot possibly be the correct or at least intended approach.
I have a manager class in my application that provides all database communications, along with extensive business logic. I've added the BackEndDataProvider interface to it and implemented the needed methods (at least, as best as I could figure since the Vaadin documentation wants to talk about ListDataProviders).
public interface AgreementManager extends KendoDataSourceManager<Agreement>, BackEndDataProvider<Agreement, GridFilter> {
}
@Service("agreementManager")
@Transactional(propagation = Propagation.REQUIRED)
public class AgreementManagerImpl extends AbstractManagerImpl implements AgreementManager {
.....
@Override
public void setSortOrders(List<QuerySortOrder> sortOrders) {
}
@Override
public int size(Query<Agreement, GridFilter> query) {
return createQueryDslStatementAndGetData(new GridDataState(
query.getFilter().orElse(null),
null,
0,
0
)).getTotal();
}
@Override
public Stream<Agreement> fetch(Query<Agreement, GridFilter> query) {
List<GridSort> sorts = new ArrayList<>();
query.getSortOrders().forEach(so ->
sorts.add(new GridSort(
so.getDirection() == SortDirection.ASCENDING ? "asc" : "desc",
so.getSorted()
))
);
GridDataState gridDataState = new GridDataState(
query.getFilter().orElse(null),
sorts.toArray(new GridSort[0]),
query.getOffset(),
query.getLimit()
);
QueryDslDataSource<Agreement> test = createQueryDslStatementAndGetData(gridDataState);
return test.getData().stream();
}
@Override
public void refreshItem(Agreement item) {
}
@Override
public void refreshAll() {
}
@Override
public Object getId(Agreement item) {
return Objects.requireNonNull(item).getId();
}
@Override
public Registration addDataProviderListener(DataProviderListener<Agreement> listener) {
return null;
}
}
And here is relevant part of the Vaadin view class where the grid is prepared:
Grid<Agreement> grid = new Grid<>(Agreement.class, false);
layout.add(grid);
grid.setMultiSort(true, Grid.MultiSortPriority.APPEND, true);
grid.setColumnReorderingAllowed(true);
grid.setPageSize(50);
/**
* Create the default header row, and then create the filter header row.
*/
grid.appendHeaderRow();
HeaderRow filterHeaderRow = grid.appendHeaderRow();
GridFilter filter = new GridFilter();
filter.setLogic("and");
ConfigurableFilterDataProvider<Agreement, Void, GridFilter> agreementManagerFilter = agreementManager.withConfigurableFilter();
agreementManagerFilter.setFilter(filter);
grid.setItems(agreementManagerFilter);
//GridLazyDataView<Agreement> gridHolder = grid.setItems(agreementManagerFilter);
{
Select<AgreementStatus> filterField = ComponentUtil
.createSelect(
false,
"",
true,
SpringUtil.getBean(AgreementStatusRepository.class).findAll(
QAgreementStatus.agreementStatus.phase.notIn(List.of(
AgreementStatus.AWARDED_RECEIVED,
AgreementStatus.AWARDED_ACCEPTED,
AgreementStatus.AWARDED_IN_PROCESS
)),
Sort.by("phase")
),
item -> item != null ? item.getDisplayName() : ""
);
filterField.addValueChangeListener(evt -> {
GridFilter gridFilter = new GridFilter();
gridFilter.setField("status.phase");
gridFilter.setValue(evt.getValue().getPhase());
gridFilter.setOperator("eq");
filter.setFilters(new GridFilter[] {
gridFilter
});
/*agreementManagerFilter.setFilter(filter);
agreementManagerFilter.refreshAll();
grid.getDataCommunicator().reset();
grid.getDataProvider().refreshAll();
gridHolder.refreshAll();*/
grid.setItems(agreementManagerFilter);
});
filterHeaderRow.getCell(
grid
.addColumn(item -> ls.getText("objectKey.contractStatus.phase." + item.getStatus().getPhase()), "status.phase")
.setKey("status")
.setHeader(ls.getText("objectKey.agreement.status"))
.setSortable(true)
.setAutoWidth(true)
).setComponent(filterField);
}
{
TextField filterField = ComponentUtil.createTextField(false, "");
filterField.setPlaceholder("Search AgreementNumber...");
filterField.setValueChangeMode(ValueChangeMode.EAGER);
filterField.addValueChangeListener(evt -> {
GridFilter gridFilter = new GridFilter();
gridFilter.setField("agreementNumber");
gridFilter.setValue(evt.getValue());
gridFilter.setOperator("contains");
filter.setFilters(new GridFilter[]{
gridFilter
});
/*agreementManagerFilter.setFilter(filter);
agreementManagerFilter.refreshAll();
grid.getDataCommunicator().reset();
grid.getDataProvider().refreshAll();
gridHolder.refreshAll();*/
grid.setItems(agreementManagerFilter);
});
filterHeaderRow.getCell(
grid
.addColumn("agreementNumber")/*.setKey("agreementNumber")*/
.setHeader(ls.getText("objectKey.agreement.agreementNumber"))
.setAutoWidth(true)
).setComponent(filterField);
}
grid.addColumn(item -> item.getAgreementStyle().getDisplayName(), "agreementStyle").setKey("agreementStyle")
.setHeader(ls.getText("objectKey.agreement.agreementStyle"))
.setAutoWidth(true);
grid.addColumn("agreementType.name")/*.setKey("agreementType")*/.setHeader(ls.getText("objectKey.agreement.contractType"))
.setAutoWidth(true);
.......
To briefly explain the above, I create a grid, get a filter wrapper of my backend provider, set the filter, set it as the items for the grid, and then I'm configuring a filter row that be used to dynamically filter the grid based on values for any to all of the columns (eventually). That dynamic filter is the key of what I'm working towards. I'd like to allow my users to filter data in the grid however they'd like based on any criteria they want to provide for any of the displayed columns. The key area of my confusion is the commented out section of the value listeners for the filter fields.
/*agreementManagerFilter.setFilter(filter);
agreementManagerFilter.refreshAll();
grid.getDataCommunicator().reset();
grid.getDataProvider().refreshAll();
gridHolder.refreshAll();*/
grid.setItems(agreementManagerFilter);
As you can see, my first attempt was to set the filter on my wrapped manager. This is in accordance with the Vaadin documentation for filtering with lazy loading -
That unfortunately didn't do anything. I tried in sequence the remaining commented out items to try and force the grid to requery the data, and nothing worked. Granted, the refreshAll DID fire correctly, and I do know that my own overridden refreshAll is not implemented. However, I have no idea what I'm supposed to do with that refreshAll that would call back to the grid and make it requery the data. As can be seen, my logic depends on getting the filter and sorts from the provided Query parameter.
In the end, my resolution was to just reset my manager by invoking grid.setItems again.
This does work and I'm getting the exact results I expect. My data filters in real-time and and displays what I'd expect.
My problem is that this feels completely wrong. That I'm forcing a desired outcome in a very roundabout way and I'm missing something obvious. My question is what is the expected/intended approach for a Vaadin grid with dynamic filters and a BackEndDataProvider to be told to refresh itself when the filters have changed?
How are you expected to do a dynamic filter of a grid using a BackEndDataProvider? I have a fully functional instance, but I feel like the way I made it work cannot possibly be the correct or at least intended approach.
I have a manager class in my application that provides all database communications, along with extensive business logic. I've added the BackEndDataProvider interface to it and implemented the needed methods (at least, as best as I could figure since the Vaadin documentation wants to talk about ListDataProviders).
public interface AgreementManager extends KendoDataSourceManager<Agreement>, BackEndDataProvider<Agreement, GridFilter> {
}
@Service("agreementManager")
@Transactional(propagation = Propagation.REQUIRED)
public class AgreementManagerImpl extends AbstractManagerImpl implements AgreementManager {
.....
@Override
public void setSortOrders(List<QuerySortOrder> sortOrders) {
}
@Override
public int size(Query<Agreement, GridFilter> query) {
return createQueryDslStatementAndGetData(new GridDataState(
query.getFilter().orElse(null),
null,
0,
0
)).getTotal();
}
@Override
public Stream<Agreement> fetch(Query<Agreement, GridFilter> query) {
List<GridSort> sorts = new ArrayList<>();
query.getSortOrders().forEach(so ->
sorts.add(new GridSort(
so.getDirection() == SortDirection.ASCENDING ? "asc" : "desc",
so.getSorted()
))
);
GridDataState gridDataState = new GridDataState(
query.getFilter().orElse(null),
sorts.toArray(new GridSort[0]),
query.getOffset(),
query.getLimit()
);
QueryDslDataSource<Agreement> test = createQueryDslStatementAndGetData(gridDataState);
return test.getData().stream();
}
@Override
public void refreshItem(Agreement item) {
}
@Override
public void refreshAll() {
}
@Override
public Object getId(Agreement item) {
return Objects.requireNonNull(item).getId();
}
@Override
public Registration addDataProviderListener(DataProviderListener<Agreement> listener) {
return null;
}
}
And here is relevant part of the Vaadin view class where the grid is prepared:
Grid<Agreement> grid = new Grid<>(Agreement.class, false);
layout.add(grid);
grid.setMultiSort(true, Grid.MultiSortPriority.APPEND, true);
grid.setColumnReorderingAllowed(true);
grid.setPageSize(50);
/**
* Create the default header row, and then create the filter header row.
*/
grid.appendHeaderRow();
HeaderRow filterHeaderRow = grid.appendHeaderRow();
GridFilter filter = new GridFilter();
filter.setLogic("and");
ConfigurableFilterDataProvider<Agreement, Void, GridFilter> agreementManagerFilter = agreementManager.withConfigurableFilter();
agreementManagerFilter.setFilter(filter);
grid.setItems(agreementManagerFilter);
//GridLazyDataView<Agreement> gridHolder = grid.setItems(agreementManagerFilter);
{
Select<AgreementStatus> filterField = ComponentUtil
.createSelect(
false,
"",
true,
SpringUtil.getBean(AgreementStatusRepository.class).findAll(
QAgreementStatus.agreementStatus.phase.notIn(List.of(
AgreementStatus.AWARDED_RECEIVED,
AgreementStatus.AWARDED_ACCEPTED,
AgreementStatus.AWARDED_IN_PROCESS
)),
Sort.by("phase")
),
item -> item != null ? item.getDisplayName() : ""
);
filterField.addValueChangeListener(evt -> {
GridFilter gridFilter = new GridFilter();
gridFilter.setField("status.phase");
gridFilter.setValue(evt.getValue().getPhase());
gridFilter.setOperator("eq");
filter.setFilters(new GridFilter[] {
gridFilter
});
/*agreementManagerFilter.setFilter(filter);
agreementManagerFilter.refreshAll();
grid.getDataCommunicator().reset();
grid.getDataProvider().refreshAll();
gridHolder.refreshAll();*/
grid.setItems(agreementManagerFilter);
});
filterHeaderRow.getCell(
grid
.addColumn(item -> ls.getText("objectKey.contractStatus.phase." + item.getStatus().getPhase()), "status.phase")
.setKey("status")
.setHeader(ls.getText("objectKey.agreement.status"))
.setSortable(true)
.setAutoWidth(true)
).setComponent(filterField);
}
{
TextField filterField = ComponentUtil.createTextField(false, "");
filterField.setPlaceholder("Search AgreementNumber...");
filterField.setValueChangeMode(ValueChangeMode.EAGER);
filterField.addValueChangeListener(evt -> {
GridFilter gridFilter = new GridFilter();
gridFilter.setField("agreementNumber");
gridFilter.setValue(evt.getValue());
gridFilter.setOperator("contains");
filter.setFilters(new GridFilter[]{
gridFilter
});
/*agreementManagerFilter.setFilter(filter);
agreementManagerFilter.refreshAll();
grid.getDataCommunicator().reset();
grid.getDataProvider().refreshAll();
gridHolder.refreshAll();*/
grid.setItems(agreementManagerFilter);
});
filterHeaderRow.getCell(
grid
.addColumn("agreementNumber")/*.setKey("agreementNumber")*/
.setHeader(ls.getText("objectKey.agreement.agreementNumber"))
.setAutoWidth(true)
).setComponent(filterField);
}
grid.addColumn(item -> item.getAgreementStyle().getDisplayName(), "agreementStyle").setKey("agreementStyle")
.setHeader(ls.getText("objectKey.agreement.agreementStyle"))
.setAutoWidth(true);
grid.addColumn("agreementType.name")/*.setKey("agreementType")*/.setHeader(ls.getText("objectKey.agreement.contractType"))
.setAutoWidth(true);
.......
To briefly explain the above, I create a grid, get a filter wrapper of my backend provider, set the filter, set it as the items for the grid, and then I'm configuring a filter row that be used to dynamically filter the grid based on values for any to all of the columns (eventually). That dynamic filter is the key of what I'm working towards. I'd like to allow my users to filter data in the grid however they'd like based on any criteria they want to provide for any of the displayed columns. The key area of my confusion is the commented out section of the value listeners for the filter fields.
/*agreementManagerFilter.setFilter(filter);
agreementManagerFilter.refreshAll();
grid.getDataCommunicator().reset();
grid.getDataProvider().refreshAll();
gridHolder.refreshAll();*/
grid.setItems(agreementManagerFilter);
As you can see, my first attempt was to set the filter on my wrapped manager. This is in accordance with the Vaadin documentation for filtering with lazy loading - https://vaadin/docs/latest/components/grid#lazy-loading
That unfortunately didn't do anything. I tried in sequence the remaining commented out items to try and force the grid to requery the data, and nothing worked. Granted, the refreshAll DID fire correctly, and I do know that my own overridden refreshAll is not implemented. However, I have no idea what I'm supposed to do with that refreshAll that would call back to the grid and make it requery the data. As can be seen, my logic depends on getting the filter and sorts from the provided Query parameter.
In the end, my resolution was to just reset my manager by invoking grid.setItems again.
This does work and I'm getting the exact results I expect. My data filters in real-time and and displays what I'd expect.
My problem is that this feels completely wrong. That I'm forcing a desired outcome in a very roundabout way and I'm missing something obvious. My question is what is the expected/intended approach for a Vaadin grid with dynamic filters and a BackEndDataProvider to be told to refresh itself when the filters have changed?
Usually you create a Filter Class with all fields you want to filter, so it might has a strong similarity to the bean you display in the grid. And the instance of the filter class can be applied to the data view, which returns when you set items to the grid component.
There is a pretty good example in the Filtering section of the documentation. https://vaadin/docs/latest/components/grid#filtering