我们目前使用的是 Vaadin Flow 版本 12.0.7(由于某些原因我们无法升级),并且我们想要覆盖 ComboBox 组件的过滤机制。我的意思是,当用户在组合框内输入输入时,我们希望更改组合框后面的项目的搜索方式。
我正在查看这个 Vaadin 文档,了解如何实现组合框的自定义过滤。更具体地说,
Filtering by a string
部分看起来很有希望。
根据 Vaadin 文档,我们实现了一个自定义接口来适应 ComboBox 的搜索方法:
public interface CustomerDataFilter {
List<Customer> fetch(int offset, int limit, String filterText);
int getCount(String filterText);
}
这非常简单,与文档示例几乎 1:1 匹配。
然后我们根据文档创建了一个方法,用数据填充 ComboBox。文档中也几乎使用了 1:1:
private DataProvider<Customer, String>
createDepartmentDataProvider(CustomerDataFilter service)
{
return DataProvider.fromFilteringCallbacks(query -> {
// getFilter returns Optional<String>
String filter = query.getFilter().orElse(null);
return service.fetch(query.getOffset(),
query.getLimit(), filter).stream();
}, query -> {
String filter = query.getFilter().orElse(null);
return service.getCount(filter);
});
}
我们在初始化 ComboBox 的数据时调用上述方法。此时我们还实现了接口(尽管这可能很难看)。然而我们不太确定如何使用这两个接口方法。我们的第一个目标是始终返回所有项目并忽略任何特定过滤,这就是我们始终返回整个
sorted
列表的原因。这只是为了测试目的,尝试我们是否更改了过滤。
public void updateCustomerList() {
List<Customer> sorted = CustomerService.getInstance().findAll();
//for initial sorting
sorted.sort(new CustomerComperator());
DataProvider<Customer, String> test = createDepartmentDataProvider(new CustomerDataFilter() {
@Override
public int getCount(String filterText) {
return 0;
}
@Override
public List<Customer> fetch(int offset, int limit, String filterText) {
return sorted;
}
});
customerCompany.setItems(sorted);
customerCompany.setDataProvider(test);
}
我们尝试将
getCount
值增加到 1
,但一旦我们将其设置为高于 0
,就会出现以下异常:
The number of items returned by the data provider exceeds the limit specified by the query (1).
我们的猜测是,我们必须以某种方式调整
getCount
和 fetch
方法,以实现我们的自定义搜索。这不会有问题,因为我们有 ComboBox 和 filterText
后面的数据。我们怎么有点困惑为什么我们还需要一个 getCount
方法以及如何绕过显示的异常?我们的想法是让 getCount
始终像 10
一样返回,并在 fetch
中返回关于 sorted
的 filterText
列表的子集。
任何人都可以详细说明/帮助我们如何实现组合框的自定义过滤或为我们指出正确的轨道吗?
感谢 Steffen Harbichs 的回答,我们找到了一种实现过滤数据提供程序的方法。基本上这就是所需的一切:
public void updateCustomerList() {
List<Customer> sorted = CustomerService.getInstance().findAll();
sorted.sort(new CustomerComperator());
customerCompany.setItems(sorted);
ListDataProvider<Customer> listTest = new ListDataProvider<>(sorted);
customerCompany.setDataProvider(listTest.filteringByPrefix(new CustomerProvider()));
}
CustomerProvider
仅实现适用的DataProvider接口。之后我们可以简单地应用 filteringByPrefix
方法来更改过滤行为。
DataProvider
的概念。
在客户端(浏览器),一旦用户与其进行交互,Vaadin 就会请求将数据显示在组合框中。请求的数据类似于“数据中有多少项?”这是 count 查询。假设您有 100 件商品。现在,下一个请求是“给我要显示的前 40 个项目”,这是 fetch 查询。组合框将显示返回的项目。一旦用户向下滚动列表,就会发出另一个数据请求“给我接下来要显示的 40 个项目”等。
总而言之,首先要求
DataProvider
返回所有项目的计数,然后根据需要逐页获取数据。当用户输入过滤器时这也适用。不同之处在于您的计数和获取查询应考虑过滤器。
示例:用户输入了过滤器“xyz”,现在只有 50 个项目与此过滤器匹配(通过名称或其他内容),因此您将在 count 方法中返回 50,并且您的 fetch 方法应该进行相应的过滤。
您遇到的异常只是指出您的 fetch 方法返回的项目比 Vaadin 请求的项目多(查询参数中的“limit”参数)。这是因为您当前不处理偏移/限制。
您有两种选择来实施您的数据提供程序:
ListDataProvider
,它是列表的 DataProvider
的实现。分页已经在实现中完成了,你不必关心它。使用 filteringBy
方法根据输入的过滤文本过滤数据。
这种方式更简单,但不可扩展。这意味着如果您有很多项目,您将消耗大量内存和 CPU,因为整个数据都是从后端检索到列表中的。如果您预计您的商品数量很少,请采用这种方法。
您可以实现自己的
DataProvider
。我建议首先扩展 AbstractBackendDataProvider
类 (javadoc)。需要实现方法sizeInBackend
和fetchFromBackend
。请注意在 fetch 方法中实现分页的偏移/限制参数。
如果您将分页委托给数据库(例如 SQL 或 no-sql 数据库),则此方法是可扩展的。内存占用会很低,因为您只在内存中保留一页。
在 Vaadin 中还有另一种方法可以实现此目的,根本不需要使用
DataProvider
。
将此视为替代方案,vaadin 文档
ComboBox<Country> comboBox = new ComboBox<>("Country");
ItemFilter<Country> filter = (country, filterString) -> country
.getName().toLowerCase().startsWith(filterString.toLowerCase());
comboBox.setItems(filter, DataService.getCountries());