Filter 组件

Filter 可以使用特殊的过滤器组件作为条件。过滤器组件需要实现 FilterComponent 接口。

默认提供三种过滤器组件:

可以注册自己的过滤器组件,参阅 过滤器组件注册

PropertyFilter

PropertyFilter - 属性过滤器 组件可以用在 Filter 组件内或单独使用。

下面例子演示了如何使用 XML 描述中的 PropertyFilter 创建设计时配置:

<data>
    <collection id="customersDc" class="ui.ex1.entity.Customer">
        <fetchPlan extends="_base">
            <property fetchPlan="_base" name="city"/>
            <property name="favouriteBrands" fetchPlan="_base"/>
        </fetchPlan>
        <loader id="customersDl">
            <query>
                <![CDATA[select e from uiex1_Customer e]]>
            </query>
        </loader>
    </collection>
</data>
<layout spacing="true" expand="customersTable">
    <filter dataLoader="customersDl"
            id="filterPropertyFilter"
            caption="PropertyFilter variations">
        <properties include=".*"/>
        <configurations>
            <configuration id="propertyConfiguration"
                           default="true"
                           name="PropertyFilter">
                <propertyFilter property="age"
                                operation="GREATER_OR_EQUAL"
                                operationEditable="true"/>
            </configuration>
        </configurations>
    </filter>
</layout>

还可以在界面控制器类中创建设计时配置:

@Autowired
protected UiComponents uiComponents;

@Autowired
protected SingleFilterSupport singleFilterSupport;
@Autowired
protected Filter pfdtcFilter;
@Autowired
protected Filter jfdtcFilter;
@Autowired
protected JpqlFilterSupport jpqlFilterSupport;
@Autowired
protected Filter gfdtcFilter;
@Subscribe
protected void onInit(InitEvent event) {
    DesignTimeConfiguration javaDefaultConfigurationPF =
            pfdtcFilter.addConfiguration("javaDefaultConfiguration",
                    "Default configuration"); (1)
    DataLoader dataLoaderPF = pfdtcFilter.getDataLoader();

    PropertyFilter<City> cityPropertyFilter =
            uiComponents.create(PropertyFilter.NAME); (2)
    cityPropertyFilter.setConditionModificationDelegated(true);
    cityPropertyFilter.setDataLoader(dataLoaderPF);
    cityPropertyFilter.setProperty("city");
    cityPropertyFilter.setOperation(PropertyFilter.Operation.EQUAL);
    cityPropertyFilter.setOperationEditable(true);
    cityPropertyFilter.setParameterName(PropertyConditionUtils.generateParameterName(
            cityPropertyFilter.getProperty()));
    cityPropertyFilter.setValueComponent(singleFilterSupport.generateValueComponent(
            dataLoaderPF.getContainer().getEntityMetaClass(),
            cityPropertyFilter.getProperty(),
            cityPropertyFilter.getOperation())); (3)
    javaDefaultConfigurationPF.getRootLogicalFilterComponent()
            .add(cityPropertyFilter); (4)

    PropertyFilter<Level> levelPropertyFilter =
            uiComponents.create(PropertyFilter.NAME);
    levelPropertyFilter.setConditionModificationDelegated(true);
    levelPropertyFilter.setDataLoader(dataLoaderPF);
    levelPropertyFilter.setProperty("level");
    levelPropertyFilter.setOperation(PropertyFilter.Operation.EQUAL);
    levelPropertyFilter.setOperationEditable(true);
    levelPropertyFilter.setParameterName(PropertyConditionUtils.generateParameterName(
            levelPropertyFilter.getProperty()));
    levelPropertyFilter.setValueComponent(singleFilterSupport.generateValueComponent(
            dataLoaderPF.getContainer().getEntityMetaClass(),
            levelPropertyFilter.getProperty(),
            levelPropertyFilter.getOperation()
    ));
    javaDefaultConfigurationPF.getRootLogicalFilterComponent().add(levelPropertyFilter);

    pfdtcFilter.setCurrentConfiguration(javaDefaultConfigurationPF); (5)
}
1 添加一个设计时配置,id 为 javaDefaultConfiguration,名称为 Default configuration
2 创建 PropertyFilter 组件,并设置其属性。
3 通过给定的 metaClass、实体属性和运算符生成过滤器值组件。
4 将创建的属性过滤器添加至 javaDefaultConfiguration 配置中。
5 javaDefaultConfiguration 设置为当前配置。

更多信息请参阅 PropertyFilter

JpqlFilter

JpqlFilter - JPQL 过滤器 是一个 UI 组件,用于过滤从 DataLoader 返回的实体。组件包含 JPQL 表达式,表达式会被添加在数据加载器查询语句的 fromwhere 部分。设置条件的布局由组件自动完成。一个 JpqlFilter 布局一般包含一个标题和一个用于编辑条件值的字段。该组件只能用在 Filter 组件内部。

下面示例演示如何使用 JpqlFilter 创建设计时配置:

<window xmlns="http://jmix.io/schema/ui/window"
        caption="msg://filterScreen.caption"
        xmlns:c="http://jmix.io/schema/ui/jpql-condition"> (1)
    <data>
        <collection id="customersDc" class="ui.ex1.entity.Customer">
            <fetchPlan extends="_base">
                <property fetchPlan="_base" name="city"/>
                <property name="favouriteBrands" fetchPlan="_base"/>
            </fetchPlan>
            <loader id="customersDl">
                <query>
                    <![CDATA[select e from uiex1_Customer e]]>
                </query>
            </loader>
        </collection>
    </data>
    <layout spacing="true" expand="customersTable">
        <filter id="filterJpqlFilter"
                dataLoader="customersDl"
                caption="JpqlFilter variations">
            <properties include=".*"/>
            <configurations>
                <configuration id="jpqlConfiguration"
                               default="true"
                               name="JpqlFilter">
                    <jpqlFilter caption="Name like"
                                parameterClass="java.lang.String">
                        <condition>
                            <c:jpql>
                                <c:where>{E}.firstName like ?</c:where> (2)
                            </c:jpql>
                        </condition>
                    </jpqlFilter>
                </configuration>
            </configurations>
        </filter>
    </layout>
</window>
1 需要添加 JPQL 条件命名空间
2 定义一个 JPQL 条件,join 元素可选,where 元素必需。

配置 JPQL 条件,需在 jpqlFilter 内定义 condition 元素,内部可使用必需的 where 元素和可选的 join 元素。下面例子中,使用 joinwhere 元素创建 jpqlFilter

<filter id="filterJpqlFilter"
        dataLoader="customersDl"
        caption="JpqlFilter variations">
    <properties include=".*"/>
    <configurations>
        <configuration id="jpqlConfigurationWithJoin"
                       name="JpqlFilter with Join">
            <jpqlFilter caption="Customers with brand"
                        parameterClass="ui.ex1.entity.Brand">
                <condition>
                    <c:jpql>
                        <c:join>join {E}.favouriteBrands i</c:join>
                        <c:where>i.id = ?</c:where>
                    </c:jpql>
                </condition>
            </jpqlFilter>
        </configuration>
    </configurations>
</filter>

filter 组件内部的 jpqlFilter 属性设置要点:

  • caption 属性可以配置过滤器中显示的条件名称。

  • defaultValue 属性设置过滤器条件的默认值。

  • 如果 JPQL 表达式包含 in (?) 条件,则 hasInExpression 属性应该设置为 true。此时,应用程序会使用 ValuesPicker 组件,以便用户能输入多个条件参数值。

    下面是使用 hasInExpression 属性的 jpqlFilter 示例:

    <filter id="filterJpqlFilter"
            dataLoader="customersDl"
            caption="JpqlFilter variations">
        <properties include=".*"/>
        <configurations>
            <configuration id="jpqlConfigurationInExpr"
                           name="JpqlFilter with IN expression">
                <jpqlFilter caption="City in"
                            parameterClass="ui.ex1.entity.City"
                            hasInExpression="true">
                    <condition>
                        <c:jpql>
                            <c:where>{E}.city in ?</c:where>
                        </c:jpql>
                    </condition>
                </jpqlFilter>
            </configuration>
        </configurations>
    </filter>
  • parameterClass 必需属性;指定条件参数的 Java 类。

  • parameterName - 查询语句中关联的参数名。可以用该名称引入配置中过滤器的相互依赖。如果未定义,则参数名随机生成。

还可以在界面控制器类中创建设计时配置:

@Autowired
protected UiComponents uiComponents;

@Autowired
protected SingleFilterSupport singleFilterSupport;
@Autowired
protected Filter pfdtcFilter;
@Autowired
protected Filter jfdtcFilter;
@Autowired
protected JpqlFilterSupport jpqlFilterSupport;
@Autowired
protected Filter gfdtcFilter;
@Subscribe
protected void onInit(InitEvent event) {
    DesignTimeConfiguration javaDefaultConfigurationJF =
            jfdtcFilter.addConfiguration("javaDefaultConfiguration",
                    "Default configuration"); (1)
    DataLoader dataLoaderJF = jfdtcFilter.getDataLoader();

    JpqlFilter<Brand> jpqlFilter =
            uiComponents.create(JpqlFilter.NAME); (2)
    jpqlFilter.setFrame(getWindow());
    jpqlFilter.setConditionModificationDelegated(true);
    jpqlFilter.setDataLoader(dataLoaderJF);
    jpqlFilter.setCondition("i.id = ?", "join {E}.favouriteBrands i");
    jpqlFilter.setParameterClass(Brand.class);
    jpqlFilter.setCaption("Select the brand");
    jpqlFilter.setParameterName(jpqlFilterSupport.generateParameterName(
            jpqlFilter.getId(),
            jpqlFilter.getParameterClass().getSimpleName()));
    jpqlFilter.setValueComponent(singleFilterSupport.generateValueComponent(
            dataLoaderJF.getContainer().getEntityMetaClass(),
            jpqlFilter.hasInExpression(),
            jpqlFilter.getParameterClass())); (3)

    javaDefaultConfigurationJF.getRootLogicalFilterComponent()
            .add(jpqlFilter); (4)
    jfdtcFilter.setCurrentConfiguration(javaDefaultConfigurationJF); (5)
}
1 添加一个设计时配置,id 为 javaDefaultConfiguration,名称为 Default configuration
2 创建 JpqlFilter 组件,并设置其属性。
3 通过给定的 metaClass 和值类型生成过滤器值组件。
4 将创建的 jpql 过滤器添加至 javaDefaultConfiguration 配置中。
5 javaDefaultConfiguration 设置为当前配置。

无参数过滤器

也可以定义没有参数的查询语句。设置 parameterClass 的值为 java.lang.Void,并使用 java.lang.Boolean 作为值:

  • true 表示 wherejoin 的 JPQL 语句会添加至数据加载器的查询语句中。

  • false 则不会加载 wherejoin 中的语句。

示例:

<filter id="parameterlessFilter"
        dataLoader="customersDl"
        caption="JPQL Filter without Parameters">
    <properties include=".*"/>
    <configurations>
        <configuration id="jpqlConfigurationNoParams"
                       name="JPQL Filter without Parameters">
            <jpqlFilter caption="Customers from London"
                        parameterClass="java.lang.Void"
                        defaultValue="true"> (1)
                <condition>
                    <c:jpql>
                        <c:join>join {E}.city c</c:join> (2)
                        <c:where>c.name = 'London'</c:where>
                    </c:jpql>
                </condition>
            </jpqlFilter>
        </configuration>
    </configurations>
</filter>
1 parameterClass="java.lang.Void" 表示过滤器无参数,defaultValue="true" 表示默认会将 wherejoin 表达式会添加至数据加载器的查询语句中
2 定义 JPQL 条件,使用可选的 join 元素和必要的 where 元素。

下面是在界面控制器中创建无参数 JPQL 过滤器的示例:

@Autowired
private Filter parameterlessFilter;

@Subscribe
protected void onInit(InitEvent event) {
    DesignTimeConfiguration javaConfiguration = parameterlessFilter
            .addConfiguration("javaConfiguration", "Java configuration");
    DataLoader dataLoader = parameterlessFilter.getDataLoader();

    JpqlFilter<Boolean> jpqlFilterNoParams = uiComponents.create(JpqlFilter.NAME);
    jpqlFilterNoParams.setFrame(getWindow());
    jpqlFilterNoParams.setConditionModificationDelegated(true);
    jpqlFilterNoParams.setDataLoader(dataLoader);
    jpqlFilterNoParams.setCondition("{E}.age > 21", null);
    jpqlFilterNoParams.setParameterClass(Void.class);
    jpqlFilterNoParams.setCaption("Customer's age > 21");
    jpqlFilterNoParams.setParameterName(jpqlFilterSupport
            .generateParameterName(jpqlFilterNoParams.getId(),
            jpqlFilterNoParams.getParameterClass().getSimpleName()));
    jpqlFilterNoParams.setValueComponent(singleFilterSupport
            .generateValueComponent(dataLoader.getContainer().getEntityMetaClass(),
            jpqlFilterNoParams.hasInExpression(),
            jpqlFilterNoParams.getParameterClass()
    ));

    jpqlFilterNoParams.setValue(true);
    javaConfiguration.setFilterComponentDefaultValue(
            jpqlFilterNoParams.getParameterName(),
            jpqlFilterNoParams.getValue());

    javaConfiguration.getRootLogicalFilterComponent().add(jpqlFilterNoParams);

    jpqlFilterNoParams.setValue(true);
    javaConfiguration.setFilterComponentDefaultValue(
            jpqlFilterNoParams.getParameterName(),
            jpqlFilterNoParams.getValue());

    javaConfiguration.getRootLogicalFilterComponent().add(jpqlFilterNoParams);
}

无参数的 JPQL 过滤器配置也可以在运行时创建:

jpql parameterless

GroupFilter

GroupFilter - 组合条件过滤器 组件是一个组合组件,用带有 ResponsiveGridLayoutGroupBoxLayout 作为根布局容器。该组件用在需要将多个条件组合成一个逻辑组时(使用逻辑运算符 ANDOR)。组件只能在 Filter 组件内部使用。

下面示例演示如何使用 GroupFilter 创建设计时配置:

<filter id="filterGroupFilter"
        dataLoader="customersDl"
        caption="GroupFilter variations">
    <properties include=".*"/>
    <configurations>
        <configuration id="groupFilter"
                       name="Simple groupFilter"
                       default="true">
            <groupFilter operation="OR">
                <propertyFilter property="age"
                                operation="GREATER_OR_EQUAL"
                                operationEditable="true"/>
                <propertyFilter property="city"
                                operation="EQUAL"
                                operationEditable="true"/>
            </groupFilter>
        </configuration>
    </configurations>
</filter>

operation 属性必需。可以用两个逻辑运算符:

  • AND 是默认运算符

  • OR

也可以在界面控制器中创建设计时配置:

@Autowired
protected UiComponents uiComponents;

@Autowired
protected SingleFilterSupport singleFilterSupport;
@Autowired
protected Filter pfdtcFilter;
@Autowired
protected Filter jfdtcFilter;
@Autowired
protected JpqlFilterSupport jpqlFilterSupport;
@Autowired
protected Filter gfdtcFilter;
@Subscribe
protected void onInit(InitEvent event) {
    DesignTimeConfiguration javaDefaultConfigurationGF =
            gfdtcFilter.addConfiguration("javaDefaultConfiguration",
                    "Default configuration"); (1)
    DataLoader dataLoaderGF = gfdtcFilter.getDataLoader();

    GroupFilter groupFilter =
            uiComponents.create(GroupFilter.NAME); (2)
    groupFilter.setConditionModificationDelegated(true);
    groupFilter.setDataLoader(dataLoaderGF);
    groupFilter.setOperation(LogicalFilterComponent.Operation.OR); (3)

    PropertyFilter<Integer> pointsPropertyFilter =
            uiComponents.create(PropertyFilter.NAME); (4)
    pointsPropertyFilter.setConditionModificationDelegated(true);
    pointsPropertyFilter.setDataLoader(dataLoaderGF);
    pointsPropertyFilter.setProperty("rewardPoints");
    pointsPropertyFilter.setOperation(PropertyFilter.Operation.GREATER_OR_EQUAL);
    pointsPropertyFilter.setOperationEditable(true);
    pointsPropertyFilter.setParameterName(PropertyConditionUtils
            .generateParameterName(pointsPropertyFilter.getProperty()));
    pointsPropertyFilter.setValueComponent(singleFilterSupport.generateValueComponent(
            dataLoaderGF.getContainer().getEntityMetaClass(),
            pointsPropertyFilter.getProperty(),
            pointsPropertyFilter.getOperation()));

    groupFilter.add(pointsPropertyFilter); (5)

    PropertyFilter<Hobby> hobbyPropertyFilter =
            uiComponents.create(PropertyFilter.NAME);
    hobbyPropertyFilter.setConditionModificationDelegated(true);
    hobbyPropertyFilter.setDataLoader(dataLoaderGF);
    hobbyPropertyFilter.setProperty("hobby");
    hobbyPropertyFilter.setOperation(PropertyFilter.Operation.EQUAL);
    hobbyPropertyFilter.setOperationEditable(true);
    hobbyPropertyFilter.setParameterName(PropertyConditionUtils.generateParameterName(
            hobbyPropertyFilter.getProperty()));
    hobbyPropertyFilter.setValueComponent(singleFilterSupport.generateValueComponent(
            dataLoaderGF.getContainer().getEntityMetaClass(),
            hobbyPropertyFilter.getProperty(),
            hobbyPropertyFilter.getOperation()
    ));
    groupFilter.add(hobbyPropertyFilter); (6)
    javaDefaultConfigurationGF.getRootLogicalFilterComponent().add(groupFilter); (7)

    PropertyFilter<Integer> agePropertyFilter =
            uiComponents.create(PropertyFilter.NAME);
    agePropertyFilter.setConditionModificationDelegated(true);
    agePropertyFilter.setDataLoader(dataLoaderGF);
    agePropertyFilter.setProperty("age");
    agePropertyFilter.setOperation(PropertyFilter.Operation.GREATER_OR_EQUAL);
    agePropertyFilter.setOperationEditable(true);
    agePropertyFilter.setParameterName(PropertyConditionUtils.generateParameterName(
            agePropertyFilter.getProperty()));
    agePropertyFilter.setValueComponent(singleFilterSupport.generateValueComponent(
            dataLoaderGF.getContainer().getEntityMetaClass(),
            agePropertyFilter.getProperty(),
            agePropertyFilter.getOperation()
    ));

    javaDefaultConfigurationGF.getRootLogicalFilterComponent().add(agePropertyFilter);

    pointsPropertyFilter.setValue(1000);
    javaDefaultConfigurationGF.setFilterComponentDefaultValue(
            pointsPropertyFilter.getParameterName(), 1000); (8)

    hobbyPropertyFilter.setValue(Hobby.FISHING);
    javaDefaultConfigurationGF.setFilterComponentDefaultValue(
            hobbyPropertyFilter.getParameterName(), Hobby.FISHING);

    agePropertyFilter.setValue(30);
    javaDefaultConfigurationGF.setFilterComponentDefaultValue(
            agePropertyFilter.getParameterName(), 30);
}
1 添加一个设计时配置,id 为 javaDefaultConfiguration,名称为 Default configuration
2 创建 GroupFilter 组件,并设置其属性。
3 groupFilter 设置 OR 逻辑运算符。
4 创建 PropertyFilter 组件,并设置其属性。
5 添加 pointsPropertyFiltergroupFilter
6 添加 hobbyPropertyFiltergroupFilter
7 将创建的组过滤器添加至 javaDefaultConfiguration 配置。
8 通过参数名为 javaDefaultConfiguration 配置设置 pointsPropertyFilter 的默认值。

注册过滤器组件

如需创建并注册 UI 过滤器组件,需要以下对象:

  • 一个组件类 - UI 组件,展示在 Filter 组件内。组件类需要继承 FilterComponent 类。参考 PropertyFilter 类作为示例。

  • 一个模型类 - 非持久化类,保存过滤器组件的状态。模型类用来保存过滤器组件状态至数据库并在运行时展示和修改过滤器组件。模型类需要继承 FilterCondition 类。参考 PropertyFilterCondition 类作为示例。

  • 一个转换器类 - 在组件和模型之间转换。转换器类需实现 FilterConverter 接口。

  • 一个编辑界面 - 模型编辑界面。如果为指定标识符,则使用默认的标识符(例如,modelName.editPropertyFilterCondition.edit)。

PropertyFilter 注册示例:

@Bean
public FilterComponentRegistration registerPropertyFilter() {
    return FilterComponentRegistrationBuilder.create(PropertyFilter.class,
            PropertyFilterCondition.class,
            PropertyFilterConverter.class)
            .withEditScreenId("ui_PropertyFilterCondition.edit")
            .build();
}

所有注册的过滤器组件都在 Add Condition(添加条件) 对话框的 弹窗按钮 中显示。

可以替换 Jmix 框架中注册的过滤器组件为自定义的组件,只需在 FilterComponentRegistration bean 使用 @Order 注解(例如,需要增加过滤器保存的模型属性集合时)。