genericFilter 通用过滤器
基本用法
genericFilter
默认会添加在某些标准视图中,例如实体的 列表视图。下图是一个典型的过滤器:
genericFilter
需要连接到一个独立的 CollectionContainer
或 KeyValueCollectionContainer
中定义的数据加载器。过滤器生成一个 Condition
对象,后续加载器使用这个条件从 数据存储 加载数据。对于 JPA 实体,数据存储会修改生成的 JPQL 查询语句,因此数据过滤是在数据库级别完成的,从数据库仅加载了过滤后的数据至应用程序内存。
下面的例子展示了基本的 genericFilter
和一些相关的组件:
<data>
<collection id="customerDc" class="com.company.onboarding.entity.Customer"> (1)
<fetchPlan extends="_base">
</fetchPlan>
<loader id="customerDl">
<query>
<![CDATA[select c from Customer c]]> (2)
</query>
</loader>
</collection>
</data>
<layout>
<genericFilter id="genericFilter" dataLoader="customerDl"> (3)
<properties include=".*"/>
</genericFilter>
<dataGrid id="customersTable"
width="100%"
dataContainer="customerDc"> (4)
<columns>
<column property="level"/>
<column property="age"/>
<column property="hobby"/>
<column property="firstName"/>
<column property="lastName"/>
<column property="rewardPoints"/>
</columns>
</dataGrid>
</layout>
1 | 数据容器包含了 Customer 实体实例的集合。 |
2 | 数据加载器用 JPQL 查询语句加载 Customer 的所有实例。 |
3 | genericFilter 组件通过 dataLoader 属性连接至数据加载器。 |
4 | 数据通过与数据容器连接的 dataGrid 组件展示。展示的数据会随着过滤器的变化而变化。 |
快速过滤示例
默认情况下,过滤器组件使用快速过滤模式。即,用户可以在使用是添加一组临时的过滤条件,仅在当前页面会话中有效。当页面关闭后,过滤条件就没有了。
假设我们有 Customer
实体,并想实现下列功能:
-
在
Customer
列表视图创建多条件的快速过滤器。 -
保存该过滤器以便将来使用。
添加条件
点击 Add search condition(添加搜索条件) 链接 [6] 打开 Add condition(添加条件) 对话框:
下列是可用的过滤条件类型:
-
Properties(属性) — 实体和关联实体的属性可以作为过滤条件。这些属性按照 properties 元素定义的顺序展示,可以包含持久化属性和 动态属性。
-
Predefined conditions(预定义条件) — 是开发者在 XML 描述的 conditions 元素中定义的条件。
添加的过滤条件会在条件区域显示。可以通过条件旁边的 删除已选条件。
创建条件
如果还没有合适的条件可供选择,也可以自己创建条件。 Create 按钮的下拉菜单中有对所有 注册 了的过滤器组件进行新建的操作。
默认支持三种此类组件:propertyFilter、jpqlFilter 和 groupFilter。
点击相应的创建按钮打开条件编辑器:
属性条件
使用 Property condition editor(属性条件编辑器) 可以将实体的一个属性作为过滤器参数并针对其进行过滤。通过 Add search condition(添加搜索条件) → + Create → Create Property condition(新建属性条件) 打开编辑器。
编辑器有以下控件和字段:
-
Property(属性) — 选择实体属性。
-
Operation(运算符) — 设置比较运算符。列表中仅显示与当前属性兼容的运算符。
-
Parameter name(参数名) — 设置查询语句参数的名称。可以用这个名称引入同一个配置中不同条件之间的依赖。如果未设置,则随机生成参数名。
-
Label(标题) — 为该条件设置一个自定义的标签,用于在面板和编辑器展示。
-
Default value(默认值) — 设置默认值。
-
Operation editable(运算符可编辑) — 支持用户选择运算类型。
-
Operation text visible(运算符标题可见) — 定义运算符文本的可见性。
-
Visible(可见性) — 定义该条件的可见性。
JPQL 条件
JPQL condition editor(JPQL 条件编辑器) 支持用户创建基于 JPQL 表达式的条件。通过 Add search condition(添加搜索条件) → + Create → Create JPQL condition(新建 JPQL 条件) 打开编辑器。
编辑器有以下控件和字段:
-
Parameter type(参数类型) — 选择条件参数的 Java 类。这里也可以选择
No parameter(无参数)
创建无参数的条件。无参数的条件与数据打标签有点类似,基于预定义的不可变规则过滤数据。
-
Label(标题) — 为该条件设置一个自定义的标签,用于在面板和编辑器展示。
-
Parameter name(参数名) — 设置查询语句参数的名称。可以用这个名称引入同一个配置中不同条件之间的依赖。如果未设置,则随机生成参数名。
-
Default value(默认值) — 设置条件的默认值。该组件在展示时,会根据 Parameter type 的设置选择兼容的组件。
-
Has IN expression(有 IN 表达式) — 定义 JPQL 表达式是否需要解析某个集合并支持
IN
表达式。 -
Visible(可见性) — 定义该条件的可见性。
-
Join — 指定一个可选的
join
表达式,该表达式会被添加在数据加载器查询语句的from
部分。如果要依赖关联实体集合的属性来创建一个复杂的过滤条件,可以用这个属性。编写该语句时:
-
以
join
或left join
关键字开头。 -
用
{E}
占位符表示选择实体。示例:
join {E}.city c
-
-
Where — 指定
where
子句,该子句会添加到数据加载器的select
查询语句中。编写该语句时:
-
用
{E}
占位符表示选择实体。 -
用
?
作为参数的占位符,需要用户输入。一个条件中最多只能有一个参数。另外,还可以有任意数量的 会话和用户属性 参数。示例:
{E}.code = ? and {E}.area = :session_area
-
组合条件
Group condition editor(组合条件编辑器) 支持用户将多个条件组合成一个逻辑组合条件。通过 Add search condition(添加搜索条件) → + Create → Create Group condition(新建组合条件) 打开编辑器。
这个编辑器有两个区域 — 一个用来配置组合的基本信息,另一个用来展示目前组合内已有的条件,并添加新条件。区域内的控件如下:
过滤器配置
配置(Configuration)是一组预先保存的过滤器条件。可以包含任何类型的条件以及其他配置。
设计时配置
设计时配置是视图 XML 中 genericFilter
的一组 内部元素。
下面的示例中,声明了带有两个设计时配置的 genericFilter
:
<genericFilter id="filterWithConfigs" dataLoader="customerDl">
<properties include=".*"/>
<configurations>
<configuration id="configuration_age_hobby" operation="AND"
name="Age AND Hobby Configuration"> (1)
<propertyFilter property="age" operation="GREATER"/>
<propertyFilter property="hobby" operation="CONTAINS"/>
</configuration>
<configuration id="configuration_level_rewards_points" operation="OR"
name="Level OR Reward Points Configuration" default="true"> (2)
<propertyFilter property="level" operation="EQUAL"/>
<propertyFilter property="rewardPoints" operation="LESS_OR_EQUAL"/>
</configuration>
</configurations>
</genericFilter>
1 | 每个配置都需要指定一个 id 属性,并且 id 的值需要在 genericFilter 的范围内唯一。如果未指定 name 属性,那么 id 将作为 消息包 中的键值使用。 |
2 | 用 default 属性为过滤器设置默认的配置。 |
如需在 Jmix Studio 中添加设计时配置,可以在界面 XML 或者 Jmix UI 结构面板中选择 genericFilter 组件,然后点击 Component Inspector 面板的 Add → Configurations → Design-time configuration 按钮。
|
设计时的配置不能在运行时修改。但是,用户仍然可以创建与设计时配置相同的运行时配置并修改其参数。这个操作通过 → Copy 实现。
当应用程序运行时,设计时配置与其他配置一同展示在下拉菜单 [4] 中。
编程式创建过滤器
下面示例演示如何通过编程的方式创建 genericFilter
过滤器配置。
@Autowired
private UiComponents uiComponents;
@ViewComponent
private VerticalLayout programmaticFilterBox;
@ViewComponent
private CollectionLoader<Customer> customerDl;
@Autowired
private SingleFilterSupport singleFilterSupport;
@Subscribe
public void onInit(final InitEvent event) {
GenericFilter genericFilter = uiComponents.create(GenericFilter.class); (1)
genericFilter.setId("programmaticFilter");
genericFilter.setDataLoader(customerDl);
genericFilter.loadConfigurationsAndApplyDefault();
DesignTimeConfiguration javaDefaultConfiguration =
genericFilter.addConfiguration("javaDefaultConfiguration",
"Default configuration"); (2)
PropertyFilter<Integer> agePropertyFilter =
uiComponents.create(PropertyFilter.class); (3)
agePropertyFilter.setConditionModificationDelegated(true);
agePropertyFilter.setDataLoader(customerDl);
agePropertyFilter.setProperty("age");
agePropertyFilter.setOperation(PropertyFilter.Operation.LESS_OR_EQUAL);
agePropertyFilter.setOperationEditable(true);
agePropertyFilter.setParameterName(PropertyConditionUtils
.generateParameterName(agePropertyFilter.getProperty()));
agePropertyFilter.setValueComponent(singleFilterSupport.generateValueComponent(
customerDl.getContainer().getEntityMetaClass(),
agePropertyFilter.getProperty(),
agePropertyFilter.getOperation())); (4)
javaDefaultConfiguration.getRootLogicalFilterComponent().add(agePropertyFilter); (5)
programmaticFilterBox.add(genericFilter); (6)
genericFilter.setCurrentConfiguration(javaDefaultConfiguration); (7)
}
1 | 用 uiComponents 工厂创建 genericFilter 。 |
2 | 添加设计时配置,方法接收两个参数:配置 id 和配置名称。 |
3 | 创建一个 PropertyFilter 并设置其属性。 |
4 | 通过给定的 metaClass、实体属性和运算符生成过滤器值组件。 |
5 | 将过滤器添加至配置中。LogicalFilterComponent 是配置的根元素。 |
6 | 将 genericFilter 放入视图的 vbox 布局中。这个布局是在视图描述中声明并注入控制器。 |
7 | 设置为当前配置。 |
XML 属性
id - alignSelf - applyShortcut - autoApply - classNames - colspan - css - dataLoader - enabled - height - maxHeight - maxWidth - minHeight - minWidth - opened - propertyHierarchyDepth - summaryText - themeNames - visible - width
applyShortcut
定义可以应用过滤器条件的键盘快捷键。仅当过滤器条件不是 自动应用 时有效。
默认值通过 jmix.ui.component.filter-apply-shortcut 应用程序属性配置。
autoApply
指定过滤器是否自动应用。
-
如果属性为
true
,则每次更改过滤器条件或过滤器失去焦点时立即应用。 -
如果属性为
false
,过滤器只有在每次点击 [1] 按钮或使用 applyShortcut 时应用。此时按钮的文字为 Apply(应用)。
默认值通过 jmix.ui.component.filter-auto-apply 属性定义。
dataLoader
为过滤器设置一个 数据加载器,基于过滤条件加载数据。
propertyHierarchyDepth
设置过滤器可以用于过滤的实体属性级别。可以指定合理的级别数。默认为 2
。
-
propertyHierarchyDepth = "1"
- 仅使用当前实体的直接属性进行过滤。 -
propertyHierarchyDepth = "2"
- 使用当前实体的属性以及子实体的直接属性进行过滤。 -
propertyHierarchyDepth = "3"
- 使用当前实体的属性以及最多至第3级子实体的属性进行过滤。
该配置可以通过 jmix.ui.component.filter-properties-hierarchy-depth 应用程序属性为所有过滤器组件进行全局配置。
事件和处理器
AttachEvent - ConfigurationChangeEvent - ConfigurationRefreshEvent - DetachEvent - OpenedChangeEvent - propertyFiltersPredicate
OpenedChangeEvent
OpenedChangeEvent
当组件的 opened 属性变化时发送。
propertyFiltersPredicate
以编程的方式包含或排除用户可以选择作为过滤条件的属性。
例如,下列代码使得用户无法选择 hobby
属性:
@Install(to = "genericFilter", subject = "propertyFiltersPredicate")
private boolean genericFilterPropertyFiltersPredicate(final MetaPropertyPath metaPropertyPath) {
return !metaPropertyPath.getMetaProperty().getName().equals("hobby");
}
XML 内部元素
actions
定义过滤器管理的操作列表。框架在 Filter Settings(过滤器设置) 中提供下列默认操作:
-
Save(保存) — 保存当前对过滤器配置的修改。由
FilterSaveAction
实现(XML 中type="genericFilter_save"
)。 -
Save with values(带值保存) — 保存当前对过滤器配置的修改。,且使用条件字段 [5] 中的值作为过滤器的默认值。
-
Save as(另存为) — 将当前过滤器配置另存为新名称。由
FilterSaveAsAction
实现(XML 中type="genericFilter_saveAs"
)。 -
Edit(编辑) — 打开编辑器编辑当前 运行时 过滤器配置。对 设计时 配置禁用。由
FilterEditAction
实现(XML 中type="genericFilter_edit"
)。 -
Remove(删除) — 删除当前 运行时 过滤器配置。对 设计时 配置禁用。由
FilterRemoveAction
实现(XML 中type="genericFilter_remove"
)。 -
Copy(复制) — 将当前配置的所有条件复制到一个新的 运行时 配置。由
FilterCopyAction
实现(XML 中type="genericFilter_copy"
)。 -
Clear values(清空值) — 清空过滤器条件值 [5]。由
FilterClearValuesAction
实现(XML 中type="genericFilter_clearValues"
)。 -
Add(添加) — 为当前过滤器配置增加条件。由
FilterAddConditionAction
实现(XML 中type="genericFilter_addCondition"
)。 -
Reset(重置) — 重置过滤器。由
FilterResetAction
实现(XML 中type="genericFilter_reset"
)。
开发者可以覆盖 菜单内的操作:
<genericFilter id="filterWithActions" dataLoader="customerDl">
<properties include=".*"/>
<actions>
<action id="addCondition" type="genericFilter_addCondition"/>
<action id="clearValues" type="genericFilter_reset"/>
</actions>
</genericFilter>
上面的示例创建了一个带 2 个操作的过滤器:
conditions
用来声明 预定义条件。
示例:
<genericFilter id="filterWithCondition" dataLoader="customerDl">
<properties include=".*"/>
<conditions>
<propertyFilter property="hobby" enabled="true" operation="STARTS_WITH"/>
</conditions>
</genericFilter>
configurations
用来声明 设计时配置。
properties
定义哪些实体属性可以用于过滤条件。该元素有下列属性:
-
include
— 设置一个正则表达式,该表达式匹配的实体属性将被包含在可选属性中。 -
exclude
— 设置一个正则表达式,该表达式匹配的实体属性将被排除在可选属性之外。 -
excludeProperties
— 设置一个英文逗号分隔的应该被排除掉的属性或属性路径列表。示例:customer.name
。 -
excludeRecursively
— 设置excludeProperties
里面定义的属性是否需要在对象图中递归地排除掉。如果设置的 true,那么属性和它的子属性都会被排除掉。
示例:
<genericFilter id="filterWithProperties" dataLoader="customerDl">
<properties include=".*"
exclude="(hobby)|(age)"
excludeProperties="id"
excludeRecursively="true"/>
</genericFilter>
可以包含或排除集合( |
过滤器条件中可以包含 动态属性。视图中不必包含 dynamicAttributes facet。 通过 + 前缀指定动态属性:
注意:如果属性是一个实体而非简单值,则不能使用实体的属性作为过滤器条件。 |
以下实体属性会被忽略:
-
由于安全权限导致无法访问的属性
-
非持久化属性
-
带
@SystemLevel
注解的属性 -
byte[]
类型的属性
responsiveSteps
根据可用空间确定搜索条件显示的列数。默认情况下,搜索条件分两列显示,当布局的宽度较小时,调整为单列。
以下过滤器覆盖了默认行为,将布局调整为一列、两列或三列,并将标签放在顶部:
<layout>
<genericFilter dataLoader="customerDl">
<responsiveSteps>
<responsiveStep minWidth="0" columns="1"/>
<responsiveStep minWidth="30em" columns="2"/>
<responsiveStep minWidth="50em" columns="3" labelsPosition="TOP"/>
</responsiveSteps>
</genericFilter>
</layout>