界面事件

本节介绍可以在控制器中处理的界面生命周期事件。章节尾部的 图表 解释了事件产生的顺序。

InitEvent

InitEvent 在界面控制器及其所有以声明方式定义的组件创建后,且完成依赖注入时发送的事件。此时,内部的界面 fragment 尚未初始化,某些可视化组件未完全初始化,例如按钮还未与操作关联起来。

@Autowired
private UiComponents uiComponents;

@Subscribe
public void onInit(InitEvent event) {
    Label<String> label = uiComponents.create(Label.TYPE_STRING);
    label.setValue("Orders list");
    getWindow().add(label);
}

AfterInitEvent

AfterInitEvent 在界面控制器及其所有以声明方式定义的组件被创建并完成依赖注入,并且所有组件都已完成其内部初始化过程时发送此事件。此时,内部的界面 fragment(如果有的话)已经发送了 InitEventAfterInitEvent 事件。在此事件监听器中,可以创建可视化组件或数据组件并且进行一些依赖于内置 fragment 的额外初始化过程。

InitEntityEvent

InitEntityEvent 在继承自 StandardEditorMasterDetailScreen 的界面中,新实体实例设置给被编辑实体的容器之前发送的事件。

使用此事件监听器初始化新实体实例中的默认值,示例:

@Subscribe
public void onInitEntity(InitEntityEvent<Order> event) {
    event.getEntity().setRating(10);
}

BeforeShowEvent

BeforeShowEvent 在界面展示之前发送的事件,此时,界面尚未被添加到应用程序 UI 中,UI 组件已经使用安全限制。在此事件监听器中,可以加载数据、检查权限和修改 UI 组件。示例:

@Autowired
private CollectionLoader<Order> ordersDl;

@Subscribe
protected void onBeforeShow(BeforeShowEvent event) {
    ordersDl.load();
}

AfterShowEvent

AfterShowEvent 在显示界面之后立即发送此事件,此时,界面已经被添加到应用程序 UI 中。在此事件监听器中,可以显示通知、对话框或其他界面。示例:

@Autowired
private Notifications notifications;

@Subscribe
protected void onAfterShow(AfterShowEvent event) {
    notifications.create().withCaption("Just opened").show();
}

BeforeCommitChangesEvent

BeforeCommitChangesEvent 在继承自 StandardEditorMasterDetailScreen 的界面中,通过 commitChanges() 方法保存数据改动之前发送。在此事件监听器中,可以做一些检查、与用户交互然后通过事件对象的 preventCommit()resume() 方法退出或者继续操作。

我们看几个用例。

  • 退出保存操作并弹出通知消息:

    @Autowired
    private Notifications notifications;
    
    @Subscribe
    public void onBeforeCommitChanges(BeforeCommitChangesEvent event) {
        if (getEditedEntity().getAmount() == null) {
            notifications.create().withCaption("Enter amount").show();
            event.preventCommit();
        }
    }
  • 退出保存,显示一个对话框,如果用户选择继续,则继续保存操作:

    @Autowired
    private Dialogs dialogs;
    
    @Subscribe
    public void onBeforeCommitChanges(BeforeCommitChangesEvent event) {
        if (getEditedEntity().getNum() == null) {
            dialogs.createOptionDialog()
                    .withCaption("Confirmation")
                    .withMessage("Number is empty. Do you want to commit?")
                    .withActions(
                            new DialogAction(DialogAction.Type.OK).withHandler(e -> {
                                event.resume();
                            }),
                            new DialogAction(DialogAction.Type.CANCEL)
                    )
                    .show();
            event.preventCommit();
        }
    }
  • 退出保存,显示对话框,用户确认之后重试 commitChanges()

    @Autowired
    private Dialogs dialogs;
    
    @Subscribe
    public void onBeforeCommitChanges(BeforeCommitChangesEvent event) {
        if (getEditedEntity().getNum() == null) {
            dialogs.createOptionDialog()
                    .withCaption("Confirmation")
                    .withMessage("Number is empty. Do you want to set default?")
                    .withActions(
                            new DialogAction(DialogAction.Type.OK).withHandler(e -> {
                                getEditedEntity().setNum(DEFAULT_NUM);
                                event.resume(commitChanges());
                            }),
                            new DialogAction(DialogAction.Type.CANCEL)
                    )
                    .show();
            event.preventCommit();
        }
    }

AfterCommitChangesEvent

AfterCommitChangesEvent 在继承自 StandardEditorMasterDetailScreen 的界面中,通过 commitChanges() 方法保存数据改动之后发送。示例:

@Autowired
private Notifications notifications;

@Subscribe
public void onAfterCommitChanges(AfterCommitChangesEvent event) {
    notifications.create()
            .withCaption("Saved!")
            .show();
}

BeforeCloseEvent

BeforeCloseEvent 在界面通过其 close(CloseAction) 方法关闭之前发送的事件。此刻界面仍然正常显示并且功能完整。在此事件监听器中,可以做任何条件检查并使用事件的 preventWindowClose() 方法阻止界面关闭,示例:

@Autowired
private CheckBox checkBox;

@Subscribe
protected void onBeforeClose(BeforeCloseEvent event) {
    if (!checkBox.isChecked()) {
        notifications.create().withCaption("Select checkbox").show();
        event.preventWindowClose();
    }
}

Window 接口内还有一个同名的事件。此事件会在界面被外部(界面控制器之外)操作关闭之前发送,比如点击窗口标签页的关闭按钮或者按下 Esc 键。可以使用 getCloseOrigin() 方法获取窗口关闭的类型,此方法返回一个实现了 CloseOrigin 接口的对象。CloseOrigin 接口的默认实现是 CloseOriginType,有三个值:

  • BREADCRUMBS - 界面通过点击面包屑链接关闭。

  • CLOSE_BUTTON - 界面通过窗口顶部的关闭按钮关闭,或者通过窗口标签页的关闭按钮或右键菜单的操作:关闭、关闭全部、关闭其他。

  • SHORTCUT - 界面通过 jmix.ui.screen.close-shortcut 应用程序属性定义的快捷键关闭。

可以在 @Subscribe 注解中指定 Target.FRAME 来订阅 Window.BeforeCloseEvent

@Subscribe(target = Target.FRAME)
protected void onBeforeClose(Window.BeforeCloseEvent event) {
    if (event.getCloseOrigin() == CloseOriginType.BREADCRUMBS) {
        event.preventWindowClose();
    }
}

AfterCloseEvent

AfterCloseEvent 在界面通过 close(CloseAction) 方法关闭并且在 Screen.AfterDetachEvent 之后发送的事件。在此事件监听器中,可以在关闭界面后显示通知或对话框,示例:

@Autowired
private Notifications notifications;

@Subscribe
protected void onAfterClose(AfterCloseEvent event) {
    notifications.create().withCaption("Just closed").show();
}

AfterDetachEvent

AfterDetachEvent 在用户关闭界面或用户注销时从应用程序 UI 中移除界面后发送的事件。此事件监听器可用于释放界面持有的资源。请注意,在 HTTP 会话过期时不会发送此事件。

UrlParamsChangedEvent

UrlParamsChangedEvent 打开的界面对应的浏览器 URL 参数更改时发送的事件。事件在显示界面之前触发,以便能够进行一些准备工作。在此事件监听器中,可以根据新的参数加载一些数据或更改界面控件的状态:

@Subscribe
protected void onUrlParamsChanged(UrlParamsChangedEvent event) {
    Map<String, String> params = event.getParams();
    notifications.create().withCaption("Params").show();
    // handle new params
}

图表

下图展示了界面生命周期事件产生的顺序。

打开界面

下图展示打开界面的流程:

open standard screen

关闭界面

下图展示关闭界面的流程:

close standard screen

打开编辑界面

下图展示打开实体编辑界面的流程:

open edit screen

关闭编辑界面

下图展示提交变更并使用 windowCommitAndClose 操作关闭编辑界面的流程:

edit screen close with save