genericFilter 通用过滤器

genericFilter 组件是一个可对数据进行动态过滤的多功能工具。

这个组件支持按照一组临时条件对数据进行 快速过滤,同时也支持创建可重复使用的过滤器 配置

  • XML 元素:genericFilter

  • Java 类:GenericFilter

基本用法

genericFilter 默认会添加在某些标准视图中,例如实体的 列表视图。下图是一个典型的过滤器:

generic filter anatomy
  1. 带下拉框的 Refresh(刷新) 按钮

  2. 比较操作符选择器。

  3. Filter Settings(过滤器设置) 按钮。

  4. 保存的过滤器和配置列表。

  5. 条件的值字段。

  6. Add search condition(添加搜索条件) 链接按钮。

genericFilter 需要连接到一个独立的 CollectionContainerKeyValueCollectionContainer 中定义的数据加载器。过滤器生成一个 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 列表视图创建多条件的快速过滤器。

  • 保存该过滤器以便将来使用。

创建快速过滤

generic filter quick

保存配置

  • 点击 generic filter settings buttonSave with values(带值保存)

  • 为过滤器配置设置名称然后点击 OK 保存:

    generic filter save

之后,可在下拉列表中选择该配置。

generic filter refresh

使用顶部的 <Reset filter>(重置过滤器) 菜单可以重置当前使用的过滤条件。

添加条件

点击 Add search condition(添加搜索条件) 链接 [6] 打开 Add condition(添加条件) 对话框:

generic filter add condition dialog

下列是可用的过滤条件类型:

  • Properties(属性) — 实体和关联实体的属性可以作为过滤条件。这些属性按照 properties 元素定义的顺序展示,可以包含持久化属性和 动态属性

  • Predefined conditions(预定义条件) — 是开发者在 XML 描述的 conditions 元素中定义的条件。

  • Configurations(过滤器配置) — 已有的过滤器配置。可以在 设计时 由开发者创建或 运行时 由用户创建。

添加的过滤条件会在条件区域显示。可以通过条件旁边的 generic filter remove 删除已选条件。

创建条件

如果还没有合适的条件可供选择,也可以自己创建条件。 Create 按钮的下拉菜单中有对所有 注册 了的过滤器组件进行新建的操作。

generic filter create button

默认支持三种此类组件:propertyFilterjpqlFiltergroupFilter

点击相应的创建按钮打开条件编辑器:

过滤器还支持 全文搜索条件,可以通过安装 搜索 扩展组件实现。

属性条件

使用 Property condition editor(属性条件编辑器) 可以将实体的一个属性作为过滤器参数并针对其进行过滤。通过 Add search condition(添加搜索条件)+ CreateCreate Property condition(新建属性条件) 打开编辑器。

generic filter property condition editor

编辑器有以下控件和字段:

  • Property(属性) — 选择实体属性。

  • Operation(运算符) — 设置比较运算符。列表中仅显示与当前属性兼容的运算符。

  • Parameter name(参数名) — 设置查询语句参数的名称。可以用这个名称引入同一个配置中不同条件之间的依赖。如果未设置,则随机生成参数名。

  • Label(标题) — 为该条件设置一个自定义的标签,用于在面板和编辑器展示。

  • Default value(默认值) — 设置默认值。

  • Operation editable(运算符可编辑) — 支持用户选择运算类型。

  • Operation text visible(运算符标题可见) — 定义运算符文本的可见性。

  • Visible(可见性) — 定义该条件的可见性。

JPQL 条件

JPQL condition editor(JPQL 条件编辑器) 支持用户创建基于 JPQL 表达式的条件。通过 Add search condition(添加搜索条件)+ CreateCreate JPQL condition(新建 JPQL 条件) 打开编辑器。

generic filter jpql condition editor

编辑器有以下控件和字段:

  • Parameter type(参数类型) — 选择条件参数的 Java 类。这里也可以选择 No parameter(无参数) 创建无参数的条件。

    generic filter jpql condition editor no parameter

    无参数的条件与数据打标签有点类似,基于预定义的不可变规则过滤数据。

  • Label(标题) — 为该条件设置一个自定义的标签,用于在面板和编辑器展示。

  • Parameter name(参数名) — 设置查询语句参数的名称。可以用这个名称引入同一个配置中不同条件之间的依赖。如果未设置,则随机生成参数名。

  • Default value(默认值) — 设置条件的默认值。该组件在展示时,会根据 Parameter type 的设置选择兼容的组件。

  • Has IN expression(有 IN 表达式) — 定义 JPQL 表达式是否需要解析某个集合并支持 IN 表达式。

  • Visible(可见性) — 定义该条件的可见性。

  • Join — 指定一个可选的 join 表达式,该表达式会被添加在数据加载器查询语句的 from 部分。如果要依赖关联实体集合的属性来创建一个复杂的过滤条件,可以用这个属性。

    编写该语句时:

    • joinleft join 关键字开头。

    • {E} 占位符表示选择实体。

      示例:

      join {E}.city c
  • Where — 指定 where 子句,该子句会添加到数据加载器的 select 查询语句中。

    编写该语句时:

    • {E} 占位符表示选择实体。

    • ? 作为参数的占位符,需要用户输入。一个条件中最多只能有一个参数。另外,还可以有任意数量的 会话和用户属性 参数。

      示例:

      {E}.code = ? and {E}.area = :session_area

组合条件

Group condition editor(组合条件编辑器) 支持用户将多个条件组合成一个逻辑组合条件。通过 Add search condition(添加搜索条件)+ CreateCreate Group condition(新建组合条件) 打开编辑器。

generic filter group condition editor

这个编辑器有两个区域 — 一个用来配置组合的基本信息,另一个用来展示目前组合内已有的条件,并添加新条件。区域内的控件如下:

组合条件

  • Operation(运算符) — 连接组合内条件的逻辑运算符。支持:ANDOR

  • Label(标题) — 为该条件设置一个自定义的标签,用于在面板和编辑器展示。如果没设置,则显示运算符的名称(ANDOR)。

  • Operation text visible(运算符标题可见) — 定义运算符文本的可见性。

  • Visible(可见性) --定义该组合条件的可见性。

条件

组合内的条件以树的形式展示。可以使用下列操作进行管理。

  • Add — 打开 Add condition(添加条件) 对话框,可以选择需要添加的条件。

  • Edit — 为选中的树节点条件打开编辑器。

  • Remove — 删除树节点中选中的所有条件。

  • generic filter move up/generic filter move down  — 修改树节点中条件位置的按钮。

过滤器配置

配置(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 面板的 AddConfigurationsDesign-time configuration 按钮。

设计时的配置不能在运行时修改。但是,用户仍然可以创建与设计时配置相同的运行时配置并修改其参数。这个操作通过 generic filter settings buttonCopy 实现。

当应用程序运行时,设计时配置与其他配置一同展示在下拉菜单 [4] 中。

generic filter design time items

运行时配置

过滤器配置可以在运行时通过 Configuration editor(配置编辑器) 进行创建。点击 generic filter settings buttonEdit 可以打开编辑器。

如需创建、编辑、删除运行时配置,用户需要有 ui.genericfilter.modifyConfiguration 权限。
generic filter run time configuration

创建运行时配置:

  • 指定一个配置名称。名称展示在下拉列表 [4] 和配置编辑器中。

  • 勾选 Available for all users(所有用户可用) 复选框设置所有用户都能使用。有 [ui.filter.modifyGlobalConfiguration] 权限的用户才能使用该复选框。

  • 勾选 Default for all users(所有用户默认) 复选框设置当用户打开界面时,是否默认选择创建的配置。仅在 Available for all users(所有用户可用) 勾选时可用。

  • 配置组合运算符并添加过滤条件。参阅 组合条件

权限

过滤器的高级管理功能需要特定的许可:

  • 如需新建、修改、删除过滤器配置,用户需要有 ui.genericfilter.modifyConfiguration 权限。

  • 如需新建、修改、删除全局(所有用户可用)过滤器配置,用户需要有 ui.genericfilter.modifyGlobalConfiguration 权限。

  • 如需新建、修改运行时 JPQL 条件,用户需要有 ui.genericfilter.modifyJpqlCondition 权限。

编程式创建过滤器

下面示例演示如何通过编程的方式创建 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 属性

applyShortcut

定义可以应用过滤器条件的键盘快捷键。仅当过滤器条件不是 自动应用 时有效。

默认值通过 jmix.ui.component.filter-apply-shortcut 应用程序属性配置。

autoApply

指定过滤器是否自动应用。

  • 如果属性为 true,则每次更改过滤器条件或过滤器失去焦点时立即应用。

  • 如果属性为 false,过滤器只有在每次点击 [1] 按钮或使用 applyShortcut 时应用。此时按钮的文字为 Apply(应用)

默认值通过 jmix.ui.component.filter-auto-apply 属性定义。

dataLoader

为过滤器设置一个 数据加载器,基于过滤条件加载数据。

opened

设置是否默认展示条件面板。

propertyHierarchyDepth

设置过滤器可以用于过滤的实体属性级别。可以指定合理的级别数。默认为 2

generic filter hierarchy depth
  • propertyHierarchyDepth = "1" - 仅使用当前实体的直接属性进行过滤。

  • propertyHierarchyDepth = "2" - 使用当前实体的属性以及子实体的直接属性进行过滤。

  • propertyHierarchyDepth = "3" - 使用当前实体的属性以及最多至第3级子实体的属性进行过滤。

该配置可以通过 jmix.ui.component.filter-properties-hierarchy-depth 应用程序属性为所有过滤器组件进行全局配置。

summaryText

设置显示在过滤器控制面板上方的简短摘要。默认值为 Filter

事件和处理器

ConfigurationChangeEvent

ConfigurationChangeEvent 当过滤器配置修改时发送。包括在没有使用配置时选择了配置,或者重置了过滤器配置。

ConfigurationRefreshEvent

ConfigurationRefreshEvent 当每次编辑过滤器配置时发送。

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(过滤器设置) generic filter settings button 中提供下列默认操作:

  • 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")。

开发者可以覆盖 generic filter settings button 菜单内的操作:

<genericFilter id="filterWithActions" dataLoader="customerDl">
    <properties include=".*"/>
    <actions>
        <action id="addCondition" type="genericFilter_addCondition"/>
        <action id="clearValues" type="genericFilter_reset"/>
    </actions>
</genericFilter>

上面的示例创建了一个带 2 个操作的过滤器:

generic filter settings menu actions

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>

可以包含或排除集合(@OneToMany@ManyToMany)属性。

过滤器条件中可以包含 动态属性。视图中不必包含 dynamicAttributes facet。

通过 + 前缀指定动态属性:

<genericFilter id="genericFilter"
               dataLoader="carsDl">
    <properties include=".*"
                excludeProperties="+passengerNumberOfSeats"/>/>
</genericFilter>

注意:如果属性是一个实体而非简单值,则不能使用实体的属性作为过滤器条件。

以下实体属性会被忽略:

  • 由于安全权限导致无法访问的属性

  • 非持久化属性

  • @SystemLevel 注解的属性

  • byte[] 类型的属性

responsiveSteps

根据可用空间确定搜索条件显示的列数。默认情况下,搜索条件分两列显示,当布局的宽度较小时,调整为单列。

以下过滤器覆盖了默认行为,将布局调整为一列、两列或三列,并将标签放在顶部:

generic filter responsive steps
<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>