EntityPicker 实体选择器

EntityPicker - 实体选择控件 在文本控件中显示 实体 实例,并在用户单击右侧的按钮时执行操作。

组件的 XML 名称:entityPicker

基本用法

满足下列情况可以使用 EntityPicker

  • 字段值是实体实例。

  • 用户需要通过查找界面或通过输入特定值选择或创建实体。

  • 用户需要为关联实体打开编辑界面。

Jmix Studio 创建实体编辑器时,会默认为实体引用属性生成 EntityPicker

entity picker1

在下面的例子中,界面为具有 customer 属性的 Order 实体定义了 id 为 orderDc 的数据容器。在 entityPicker 元素中,通过 dataContainer 属性连接到 orderDc 数据容器,并给 property 属性设置了 customer 实体属性。实体属性是另一实体的引用,在上面的示例中就是 Customer 实体。

<data>
    <instance id="orderDc"
              class="ui.ex1.entity.Order">
        <fetchPlan extends="_base">
            <property name="customer" fetchPlan="_instance_name"/>
        </fetchPlan>
        <loader/>
    </instance>
</data>
<layout spacing="true">
    <vbox spacing="true" id="vbox">
        <entityPicker id="customerField"
                      dataContainer="orderDc"
                      property="customer"
                      caption="msg://ui.ex1.entity/Order.customer">
            <actions>
                <action id="lookup" type="entity_lookup"/>
                <action id="clear" type="entity_clear"/>
            </actions>
        </entityPicker>
    </vbox>
</layout>

为了让 EntityPicker 能正常工作,需要设置 metaClass 属性或同时设置 dataContainerproperty 属性。

操作

可以给 EntityPicker 设置自定义或预定义的操作,操作作为按钮显示在右侧。 定义操作可以在 XML 中用 actions 元素或者在控制器中用 addAction() 方法编程式添加。

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

预定义操作

当 Studio 在编辑界面生成 EntityPicker 时,还会同时生成两个预定义的操作:entity_lookupentity_clear。此外,还支持 entity_openentity_openComposition 操作。

typeid 属性在 XML 中配置预定义操作。

如果创建无操作的 EntityPicker,XML 加载器仅定义 entity_lookupentity_clear 操作。如需添加其他预定义操作,例如,entity_open,可以按照下面这样指定 actions 元素:

<entityPicker dataContainer="orderDc"
              property="customer"
              caption="msg://ui.ex1.entity/Order.customer">
    <actions>
        <action id="lookup" type="entity_lookup"/>
        <action id="open" type="entity_open"/>
        <action id="clear" type="entity_clear"/>
    </actions>
</entityPicker>

actions 元素不是对标准操作进行扩展,而是进行覆盖。因此,需要明确定义所有必需操作的标识符。

自定义操作

如需在 XML 中定义自定义操作,可以使用内部的 actions 元素。为 action 指定 idicon 属性:

<entityPicker id="customerEntityPicker"
              property="customer"
              dataContainer="orderDc">
    <actions>
        <action id="lookup" type="entity_lookup"/>
        <action id="points"
                icon="QUESTION"
                description="msg://knowPoints"/>
    </actions>
</entityPicker>

然后在界面控制器订阅 Action.ActionPerformedEvent 实现自定义逻辑:

@Autowired
private EntityPicker<Customer> customerEntityPicker;
@Autowired
private Notifications notifications;

@Subscribe("customerEntityPicker.points")  (1)
public void onCustomerEntityPickerPoints(Action.ActionPerformedEvent event) {
    Customer customer = customerEntityPicker.getValue();
    if (customer != null) {
        notifications.create()
                .withCaption(customer.getFirstName() +
                        " has " + customer.getRewardPoints() +
                        " reward points")
                .show();
    } else {
        notifications.create()
                .withCaption("Choose a customer")
                .show();
    }
}
1 @Subscribe 注解包含 EntityPickerid 以及操作的 id,以“.”分隔。

可以用 Studio 生成处理 Action.ActionPerformedEvent 的桩代码。

编程式添加操作

使用 addAction() 以编程方式添加操作。

  • 添加标准操作

    例如,如果在组件的 XML 描述中没有内部的 actions 元素,就可以使用这个方法添加缺少的标准操作:

    @Autowired
    private Actions actions;
    @Autowired
    private EntityPicker<Customer> entityPicker;
    
    @Subscribe
    public void onInit(InitEvent event) {
        entityPicker.addAction(actions.create(EntityOpenAction.class));
    }
  • 添加自定义操作

    创建自定义操作的示例:

    @Autowired
    private EntityPicker<Customer> customerEp;
    
    @Subscribe
    public void onInit(InitEvent event) {
        customerEp.addAction(new BaseAction("showLevel")
                .withCaption(null)
                .withDescription(null)
                .withIcon(JmixIcon.VIEW_ACTION.source())
                .withHandler(e -> {
                    Customer customer = customerEp.getValue();
                    if (customer != null) {
                        notifications.create()
                                .withCaption(customer.getFirstName() + " " +
                                        customer.getLastName() +
                                        "'s level is " + customer.getLevel())
                                .show();
                    } else {
                        notifications.create()
                                .withCaption("Choose a customer")
                                .show();
                    }
                }));
    }

MetaClass

可以在不绑定数据容器的情况下使用 EntityPicker,即不设置 dataContainerproperty 属性。在这种情况下,需要使用 metaClass 属性指定 EntityPicker 的实体类型。示例:

<entityPicker id="custPicker" metaClass="uiex1_Customer">
    <actions>
        <action id="lookup" type="entity_lookup"/>
        <action id="open" type="entity_open"/>
    </actions>
</entityPicker>

可以通过将组件注入控制器并调用其 getValue() 方法来获取所选实体的实例。

设置图标

EntityPicker 左侧可以有图标。只需要在界面控制器注册 fieldIconProvider 并实现自定义逻辑:

@Install(to = "customerField", subject = "fieldIconProvider")
private String customerFieldFieldIconProvider(Customer customer) { (1)
    return (customer != null) ? "font-icon:CHECK" : "font-icon:BAN";
}
1 fieldIconProvider 代理方法实现。
entity picker icons

参阅 图标 了解更多关于使用图标的内容。

如需编程式注册字段的图标 provider,使用组件的 setFieldIconProvider() 方法。

输入文本值

默认情况下,用户不能手动输入值。如果设置 fieldEditable 属性为 true,则启用手动输入功能。这个功能可以用来根据用户输入的值创建实体实例。

需要注意,输入的值不会设置给数据模型。用 FieldValueChangeEvent 处理用户输入。

下面的例子中,我们有 Address 实体,此实体的 country 属性关联至 Country 实体。Country 实体只有一个 name 属性。我们在 XML 中定义 countryField 的属性 fieldEditable="true"

<entityPicker id="countryField" property="country" fieldEditable="true">
    <actions>
        <action id="lookup" type="entity_lookup"/>
        <action id="clear" type="entity_clear"/>
    </actions>
</entityPicker>

现在用户可以手动输入值了。如需处理该值(字符串格式),在控制器中订阅 FieldValueChangeEvent 事件:

@Autowired
private DataContext dataContext;

private Country country;

@Autowired
private TextField<String> addressField;

@Autowired
private EntityPicker<Country> countryField;

@Autowired
private Dialogs dialogs;

@Subscribe("countryField") (1)
public void onCountryFieldFieldValueChange(ValuePicker.FieldValueChangeEvent<Country>
                                                       event) {
    String value = event.getText(); (2)
    if (!Strings.isNullOrEmpty(value)) {
        country = dataContext.create(Country.class); (3)
        country.setName(value);
        countryField.setValue(country); (4)
    }
}
1 @Subscribe 注解包含 EntityPicker id。
2 从事件对象中获取输入的值。
3 创建一个 Country 实体实例。
4 将创建的实例设置给该字控件。

如需以编程的方式注册事件处理器,使用组件的 addFieldValueChangeListener() 方法。

编程式创建 EntityPicker

如需在控制器中创建 EntityPicker,可以使用 UiComponents 工厂。需要注意的是,以编程方式创建的 EntityPicker 无默认操作,需要手动添加必须的操作:

@Autowired
private Metadata metadata;
@Autowired
private UiComponents uiComponents;
@Autowired
private Actions actions;
@Subscribe
public void onInit(InitEvent event) {
    EntityPicker<User> userPicker = uiComponents.create(EntityPicker.of(User.class));
    userPicker.setMetaClass(metadata.getClass(User.class));
    userPicker.addAction(actions.create(EntityLookupAction.class));
    userPicker.addAction(actions.create(EntityOpenAction.class));
    userPicker.addAction(actions.create(EntityClearAction.class));
    vbox.add(userPicker);
}

事件和处理器

如需使用 Jmix Studio 生成处理器的桩代码,需要在界面 XML 描述或者 Jmix UI 层级结构面板选中该组件,然后用 Jmix UI 组件面板的 Handlers 标签页生成。

或者可以使用界面控制器顶部面板的 Generate Handler 按钮。

ContextHelpIconClickHandler

FieldIconProvider

参阅 Setting Icons

FieldValueChangeEvent

FieldValueChangeEvent 当用户手动输入值时发送。

更多信息,请参阅 Entering a Text Value

Formatter

为组件添加一个 formatter

下面的例子中,我们展示 customerFormat 实体选择控件 formatter 的用法:

@Install(to = "customerFormat", subject = "formatter")
protected String customerFormatFormatter(Customer value) {
    return value != null ? value.getFirstName() + " " + value.getLastName() : null;
}

如需以编程的方式添加 formatter,使用组件的 addFormatter() 方法。

Validator

参阅 Validator

ValueChangeEvent

参阅 ValueChangeEvent

EntityPicker XML 属性

EntityPicker XML 元素