添加

AddAction 是一个 列表操作,将已有的实体实例添加到数据容器,要添加的实例通过在查找界面进行选择。比如,可以用来填写多对多的集合。

该操作通过 io.jmix.ui.action.list.AddAction 类实现,在 XML 中需要使用操作属性 type="add" 定义。可以用 action 元素的 XML 属性定义通用的操作参数,参阅 声明式操作 了解细节。下面我们介绍 AddAction 类特有的参数。

属性

下列参数可以在 XML 或 Java 中设置:

  • openMode - 查找界面的打开模式,是 OpenMode 枚举类型的值:NEW_TABDIALOG 等。默认情况下,AddActionTHIS_TAB 模式打开查找界面。

  • screenId - 查找界面的字符串 id。AddAction 默认会使用带有 @PrimaryLookupScreen 注解的界面,或标识符为 <entity_name>.browse 格式的界面,例如,demo_Customer.browse

  • screenClass - 查找界面控制器的 Java 类。比 screenId 有更高的优先级。

示例,需要以对话框方式打开一个特定的查找界面,可以在 XML 中这样配置操作:

<action id="add" type="add">
    <properties>
        <property name="openMode" value="DIALOG"/>
        <property name="screenClass" value="ui.ex1.screen.entity.brand.BrandBrowse"/>
    </properties>
</action>

或者,可以在界面控制器注入该操作,然后用 setter 配置:

@Named("brandsTable.add")
private AddAction<Brand> addAction;

@Subscribe
public void onInitEntity(InitEntityEvent<Customer> event) {
    addAction.setOpenMode(OpenMode.DIALOG);
    addAction.setScreenClass(BrandBrowse.class);
}

处理器

下面了解这些只能用 Java 配置的参数。用 Studio 能为这些参数正确生成带注解的方法。

screenOptionsSupplier

返回 ScreenOptions 对象,返回值可以传递给打开的查找界面。示例:

@Install(to = "brandsTable.add", subject = "screenOptionsSupplier")
private ScreenOptions brandsTableAddScreenOptionsSupplier() {
    return new MapScreenOptions(ParamsMap.of("someParameter", 10));
}

返回的 ScreenOptions 对象可以在打开界面的 InitEvent 中使用。

screenConfigurer

接收查找界面作为参数并能在打开之前初始化界面。示例:

@Install(to = "brandsTable.add", subject = "screenConfigurer")
private void brandsTableAddScreenConfigurer(Screen screen) {
    ((BrandBrowse) screen).setSomeParameter(10);
}

注意,界面 configurer 会在界面已经初始化但是还未显示时生效,即在界面的 InitEventAfterInitEvent 事件之后,但是在 BeforeShowEvent 之前。

selectValidator

当用户在查找界面点击 Select 时调用,参数中包含选择的实体。可以用这个处理器检查选中的条目是否满足某些条件。处理器必须返回 true 才能继续并关闭查找界面。示例:

@Install(to = "brandsTable.add", subject = "selectValidator")
private boolean brandsTableAddSelectValidator(LookupScreen.ValidationContext<Brand> validationContext) {
    boolean valid = checkBrands(validationContext.getSelectedItems());
    if (!valid) {
        notifications.create().withCaption("Selection is not valid").show();
    }
    return valid;
}

transformation

在查找界面选择实体并验证之后调用。接收选中的实体集合作为参数。可以用这个处理器对选中的实体做一些处理,然后再传递给接收的数据容器。示例:

@Install(to = "brandsTable.add", subject = "transformation")
private Collection<Brand> brandsTableAddTransformation(Collection<Brand> collection) {
    return reloadBrands(collection);
}

afterCloseHandler

在查找界面关闭后调用的处理器。AfterCloseEvent 事件会传递给该处理器。示例:

@Install(to = "brandsTable.add", subject = "afterCloseHandler")
private void brandsTableAddAfterCloseHandler(AfterCloseEvent afterCloseEvent) {
    if (afterCloseEvent.closedWith(StandardOutcome.SELECT)) {
        System.out.println("Selected");
    }
}

使用 ActionPerformedEvent

如果需要在该操作执行前做一些检查或者与用户做一些交互,可以订阅操作的 ActionPerformedEvent 事件并按需调用操作的 execute() 方法。操作会使用你为它定义的所有参数进行调用。下面的例子中,我们在执行操作前展示了一个确认对话框:

@Subscribe("brandsTable.add")
public void onBrandsTableAdd(Action.ActionPerformedEvent event) {
    dialogs.createOptionDialog()
            .withCaption("Please confirm")
            .withMessage("Do you really want to add a brand?")
            .withActions(
                    new DialogAction(DialogAction.Type.YES)
                            .withHandler(e -> addAction.execute()), // execute action
                    new DialogAction(DialogAction.Type.NO)
            )
            .show();
}

另外,还可以先订阅 ActionPerformedEvent,但是不调用操作的 execute() 方法,而是使用 ScreenBuilders API 直接打开查找界面。此时,会忽略所有特殊的操作参数和行为,只能用其通用参数,比如 captionicon 等。示例:

@Subscribe("brandsTable.addAction")
public void onBrandsTableAddAction(Action.ActionPerformedEvent event) {
    screenBuilders.lookup(brandsTable)
            .withOpenMode(OpenMode.DIALOG)
            .withScreenClass(BrandBrowse.class)
            .withSelectValidator(customerValidationContext -> {
                boolean valid = checkBrands(customerValidationContext.getSelectedItems());
                if (!valid) {
                    notifications.create().withCaption("Selection is not valid").show();
                }
                return valid;

            })
            .build()
            .show();
}