Filter Components

Filter can use special filter components as conditions. Filter components should implement the FilterComponent interface.

There are three filter components available by default:

You can register your own filter component, see Filter Component Registration.

PropertyFilter

The PropertyFilter component can be used inside the Filter component and independently.

The example below demonstrates how to create a design-time configuration with PropertyFilter in the XML descriptor:

<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>

To add propertyFilter in Jmix Studio, select the Filter component in the screen descriptor XML or in the Component Hierarchy panel and click on the Add→Conditions→PropertyFilter button in the Component Inspector panel.

You can also create a design-time configuration in the screen controller class:

@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 Adds a design-time configuration with the javaDefaultConfiguration id and the Default configuration name.
2 Creates the PropertyFilter component and sets its properties.
3 Generates the filter value component by the given metaClass, entity property and operation.
4 Adds the created property filter to the javaDefaultConfiguration configuration.
5 Sets the javaDefaultConfiguration configuration as current.

See the additional information in PropertyFilter.

JpqlFilter

JpqlFilter is a UI component used for filtering entities returned by the DataLoader. The component contains JPQL expressions that will be added to the from and where data loader query sections. The component can automatically render the proper layout for setting a condition value. A JpqlFilter layout contains a label with a caption and a field for editing a condition value in the general case. The component can be used only inside the Filter component.

The example below demonstrates how to create a design-time configuration with a 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 You should add the JPQL conditions namespace.
2 Define a JPQL condition with optional join element and mandatory where element.

To configure JPQL conditions, define the condition element inside jpqlFilter with optional join element and mandatory where element. In the example below, we create jpqlFilter with the join and where elements:

<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>

jpqlFilter attributes inside the filter component:

  • You can set the condition name displayed in filter, using the caption attribute.

  • You can use the defaultValue attribute to set the default value for the filter condition.

  • The hasInExpression attribute should be set to true if the JPQL expression contains in (?) conditions. In this case, the application will use the ValuesPicker component. So the user will be able to enter several condition parameter values.

    Below is an example of jpqlFilter with the hasInExpression attribute:

    <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 is a required attribute; it defines the Java class of the condition parameter.

  • parameterName - the name of the associated query parameter. You can use this name to introduce dependencies between filter components in configuration. If not defined, then the parameter name is randomly generated.

To add jpqlFilter in Jmix Studio, select the Filter component in the screen descriptor XML or in the Component Hierarchy panel and click on the Add→Conditions→JPQLFilter button in the Component Inspector panel.

You can also create a design-time configuration in the screen controller class:

@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 Adds a design-time configuration with the javaDefaultConfiguration id and the Default configuration name.
2 Creates the JpqlFilter component and sets its properties.
3 Generates the filter value component by the given metaClass, and value type.
4 Adds the created jpql filter to the javaDefaultConfiguration configuration.
5 Sets the javaDefaultConfiguration configuration as current.

Filter without Parameters

It is possible to define a query without parameters. To do so, set the parameterClass value to java.lang.Void, and use java.lang.Boolean as a value:

  • true value means that JPQL expressions from where and join sections will be added to the data loader query.

  • false will not load expressions from where and join sections.

Example:

<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" makes the filter parameterless, and defaultValue="true" defines that the join and where expressions will be added to the data loader query by default.
2 Define a JPQL condition with optional join element and mandatory where element.

Below is an example of creating a parameterless JPQL filter in the screen controller class:

@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);
}

The JPQL filter configuration without parameters can also be created at run-time:

jpql parameterless

GroupFilter

The GroupFilter component is a composite component that has a GroupBoxLayout with a ResponsiveGridLayout as its root container. This component is needed to combine several conditions into a logical group, using logical operators (AND or OR). The component can be used only inside the Filter component.

The example below demonstrates how to create a design-time configuration with a 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>

The operation attribute is required. Two logical operations are available:

  • AND is the default operation

  • OR

To add groupFilter in Jmix Studio, select the Filter component in the screen descriptor XML or in the Component Hierarchy panel and click on the Add→Conditions→GroupFilter button in the Component Inspector panel.

You can also create a design-time configuration in the screen controller class:

@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 Adds a design-time configuration with the javaDefaultConfiguration id and the Default configuration name.
2 Creates the GroupFilter component and sets its properties.
3 Sets the OR logical operation for groupFilter.
4 Creates the PropertyFilter component and sets its properties.
5 Adds pointsPropertyFilter to groupFilter.
6 Adds hobbyPropertyFilter to groupFilter.
7 Adds the created group filter to the javaDefaultConfiguration configuration.
8 Sets a default value of pointsPropertyFilter for the javaDefaultConfiguration configuration by the parameter name.

Filter Component Registration

To create and register a UI filter component in the framework, you need the following objects:

  • A component class - a UI component that will be displayed inside the Filter component. A component class should extend the FilterComponent class. As an example of a component class, consider the PropertyFilter class.

  • A model class - a non-persistent class that stores the state of the filter component. A model class is used to save the filter component state in DB and display and change the state of the filter component at run-time. A model class should extend the FilterCondition class. As an example of a model class, consider the PropertyFilterCondition class.

  • A converter class is needed for converting between a component and a model. A converter class should implement the FilterConverter interface.

  • An edit screen - the model edit screen. If no identifier is specified, then the default identifier (for example, modelName.edit, PropertyFilterCondition.edit) will be used.

PropertyFilter registration example:

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

All registered filter components are displayed in a popup button on the Add Condition dialog.

You can replace a filter component registered in the Jmix framework with your own implementation by specifying the @Order annotation on the FilterComponentRegistration bean (for example, to expand the set of model attributes saved by the filter).

All XML Attributes