entityComboBox 实体下拉框

支持用户从下拉列表中选择单一 实体 实例并进行相关操作。操作可以是预定义的操作,例如更新实例的详细信息,或自定义的操作以完成其他一些任务。

事实上,entityComboBoxcomboBoxentityPicker 的组合。

  • XML 元素:entityComboBox

  • Java 类:EntityComboBox

基本用法

当字段值是实体对象时,通常使用 entityComboBox 组件。支持用户选择特定实体实例并执行相关操作。

点击控件或箭头按钮可以打开一个显示实体实例的下拉框:

entity combobox basics
entity combobox basics 2

请注意,除非您配置了懒加载,否则该组件会将选项中所有实例的列表加载到浏览器的内存和服务器的内存中。这可能会消耗更多系统资源,特别是加载大量选项时,甚至会影响性能。此外,选项过多也会让用户难以找到特定的实例。

如果选项过多,例如几千或更多,又或者在选择实例时需要提供更多的信息,那么建议使用 entityPicker

简单用例

下面的示例演示如何使用 entityCombobox 组件选择用户所属的部门:

<data>
    <instance class="com.company.onboarding.entity.User"
              id="userDc"> (1)
        <fetchPlan extends="_base"> (2)
            <property name="department" fetchPlan="_base"/>
        </fetchPlan>
        <loader id="userDl"/>
    </instance>
    <collection class="com.company.onboarding.entity.Department"
                id="departmentsDc"> (3)
        <fetchPlan extends="_base"/>
        <loader id="departmentsDl">
            <query>
                <![CDATA[select e from Department e]]>
            </query>
        </loader>
    </collection>
</data>
<facets>
    <dataLoadCoordinator auto="true"/> (4)
</facets>
<layout>
    <entityComboBox dataContainer="userDc"
                    property="department"
                    itemsContainer="departmentsDc"> (5)
        <actions>
            <action id="entityClear" type="entity_clear"/> (6)
        </actions>
    </entityComboBox>
</layout>
1 保存当前正在编辑的 User 实例的数据容器。
2 扩展了 fetch plan,以获取 Department 实例集合作为选项。
3 保存所有现有 Department 实例集合的数据容器。
4 数据加载协调器,自动为组件提供实例选项。
5 将组件与数据容器和属性绑定。指定选项列表的容器。
6 添加预定义操作,用于清除所选内容。

有关 entityComboBox 及其变体的更多交互式示例,请参阅:

数据绑定

数据绑定是指将组件与数据容器进行关联。可视化组件或相应数据容器中的更改可以触发彼此的更新。有关详细信息,请参阅 使用数据组件

选择一个实例

如果只是简单的需要选择一个实体实例,那么可以通过 metaClass 属性指定实体。如果需要从一组特定的实体实例中选择,则可以通过 itemsContainer 属性指定。

<entityComboBox metaClass="Department"
                itemsContainer="departmentsDc">
    // ...
</entityComboBox>

更新关联属性

选择实体实例通常用来更新另一个实体中的一个属性。在 上面的示例 中,通过选择 Department 实例更新了 User 实例中的部门属性。

此时,需要将组件绑定至父实体的数据容器,并指定需要更新的属性,这两个步骤分别通过 dataContainerproperty 属性实现。通过 itemsContainer 属性指定选项列表。

<entityComboBox dataContainer="userDc"
                property="department"
                itemsContainer="departmentsDc">
    // ...
</entityComboBox>

操作

entityComboBox 初始化时并不带任何操作,需要显式地添加,示例:

<entityComboBox dataContainer="userDc"
                property="department"
                itemsContainer="departmentsDc">
    <actions>
        <action id="entityClear" type="entity_clear"/>
        <action id="entityLookup" type="entity_lookup"/>
        <action id="entityOpen" type="entity_open"/>
    </actions>
</entityComboBox>

如需在 Jmix Studio 中添加 action,可以在视图 XML 或者 Jmix UI 结构面板中选择组件,然后在组件面板点击 Add→Action 按钮。

自定义和预定义操作的详细信息请参阅 entityPicker操作 部分。

延迟加载

该组件支持根据用户输入批量加载选项,而不是一次加载全部选项。这样可以确保流畅的用户体验,即便选项数量非常庞大。

声明式配置

若要在视图 XML 中实现延迟加载,需要配置 itemsQuery 属性,而不是 itemsContainer。例如,要最多加载 30 个选项并在列表中显示,可按照下列代码:

<entityComboBox metaClass="Department" pageSize="30"> (1)
    <itemsQuery class="com.company.onboarding.entity.User"
                searchStringFormat="(?i)%${inputString}%"
                escapeValueForLike="true"
                fetchPlan="_instance_name"> (2)
        <query>
            <![CDATA[select e from Department e where e.name like :searchString order by e.name]]> (3)
        </query>
    </itemsQuery>
</entityComboBox>
1 pageSize 属性定义每次批量加载的数据量。默认为 50。
2 itemsQuery 中的属性可以控制加载的过程。解释如下:
  • class - 指定需要加载实体的全限定名称。

  • searchStringFormat - 包含变量占位符的一个字符串,占位符后续会替换为具体值。

  • escapeValueForLike - 设置是否搜索包含特定符号的值。默认为 false

  • fetchPlan - 可选参数,指定 fetch plan。

3 JPQL 查询语句。

编程式配置

选项的加载过程也可以通过代码在 itemsFetchCallback 处理方法中配置。示例:

<entityComboBox id="departmentComboBox" metaClass="Department"/>
@ViewComponent
private EntityComboBox<Department> departmentField;

@Install(to = "departmentComboBox", subject = "itemsFetchCallback")
private Stream<Department> departmentFieldItemsFetchCallback(final Query<Department, String> query) {
    String param = query.getFilter().orElse("");
    return dataManager.load(Department.class)
            .condition(PropertyCondition.contains("name", param))
            .firstResult(query.getOffset())
            .maxResults(query.getLimit())
            .list()
            .stream();
}

示例中,数据通过 DataManager 获取,但是也可以修改为通过自定义服务获取。

自定义渲染器

默认情况下,下拉框中的实体实例是以纯文本进行显示。可以自定义渲染器,实现下拉框选项的渲染逻辑,比如可以包含组件、图标、甚至布局。

例如,定义下面的渲染器在部门名称前添加一个图标:

@Supply(to = "customRendererField", subject = "renderer")
private Renderer<Department> departmentRenderer() {
    return new ComponentRenderer<>(department -> {
        Icon icon = VaadinIcon.USERS.create();
        HorizontalLayout contentBox = uiComponents.create(HorizontalLayout.class);
        contentBox.setPadding(false);
        contentBox.add(icon);
        contentBox.add(department.getName());
        return contentBox;
    });
}

XML 属性

allowCustomValue

如果 allowCustomValue 属性为 true,用户可以输入不匹配任何选项的字符串值,此时会触发 CustomValueSetEvent

注意,entityComboBox 不会自动处理自定义字符串。请使用 CustomValueSetEvent 进行处理。

默认值为 false

itemsContainer

设置包含选项列表的数据容器名称。组件将实体的 实例名 作为选项展示。

opened

设置下拉列表是否默认打开。

默认值为 false

事件和处理器

在 Jmix Studio 生成处理器桩代码时,可以使用 Jmix UI 组件面板的 Handlers 标签页或者视图类顶部面板的 Generate Handler 添加,也可以通过 CodeGenerate 菜单(Alt+Insert / Cmd+N)生成。

CustomValueSetEvent

当用户输入非空值,且不匹配任何已有选项时,发出 com.vaadin.flow.component.combobox.ComboBoxBase.CustomValueSetEvent 事件。如需启用自定义输入,请设置 allowCustomValue 属性为 true

<entityComboBox dataContainer="userDc"
                property="department"
                itemsContainer="departmentsDc"
                id="departmentField"
                allowCustomValue="true"/>
@ViewComponent
private CollectionContainer<Department> departmentsDc;

@ViewComponent
private EntityComboBox<Department> departmentField;

@Autowired
private DataManager dataManager;

@Subscribe("departmentField")
public void onDepartmentFieldCustomValueSet(ComboBoxBase.CustomValueSetEvent
                                                    <ComboBox<Department>> event) {
    Department department = dataManager.create(Department.class); (1)
    department.setName(event.getDetail()); (2)
    departmentsDc.getMutableItems().add(department); (3)
    departmentField.setValue(department);
}
1 创建一个新实例合并至上下文。
2 为新创建的 department 实体设置名称。
3 添加合并的实体。

XML 内部元素