对话框

Dialogs 接口用来展示标准对话框窗口。对话框窗口是覆盖于其他界面之上的小窗口,可以展示一些信息或 UI 元素。

createMessageDialog()createOptionDialog()createInputDialog() 方法是流式 API 的入口点,可以用来创建和显示对话框。

Dialogs 的外观可以使用带 $jmix-window-modal-* 前缀的 SCSS 变量进行自定义。在创建了 自定义主题 之后,可以在可视化编辑器中修改这些变量。

消息对话框

消息对话框的最基本用法是为用户展示一些信息。

message dialog

下面的例子中,当用户点击按钮时,会显示一个消息对话框:

@Autowired
private Dialogs dialogs;

@Subscribe("msgDialogBtn")
public void onMsgDialogBtnClick(Button.ClickEvent event) {
    dialogs.createMessageDialog()
            .withCaption("Success")
            .withMessage("Your invitation successfully send")
            .show();
}

使用 withCaption() 方法设置对话框标题。

使用 withMessage() 方法传递消息文本。

使用 withContentMode() 方法可以定义消息文本的展示方式。有三种预定义的内容模式:

  • ContentMode.TEXT - 文本值作为纯文本展示。

  • ContentMode.PREFORMATTED - 文本值作为预格式化文本展示。该模式下,会保留换行并在界面展示。

  • ContentMode.HTML - 文本值作为 HTML 进行解析和展示。当使用 HTML 时,别忘了转义内容以防恶意代码注入。

可以在消息中使用 \n 字符来换行。如果要显示 HTML,可以用 withContentMode() 方法带 ContentMode.HTML 参数。

withHtmlSanitizer() 方法传参 true 可以启用对话框内容的 HTML 清理功能。此时,必须为 withContentMode() 方法传递 ContentMode.HTML 参数。

protected static final String UNSAFE_HTML = "<i>Jackdaws </i><u>love</u> " +
        "<font size=\"javascript:alert(1)\" " +
        "color=\"moccasin\">my</font> " +
        "<font size=\"7\">big</font> <sup>sphinx</sup> " +
        "<font face=\"Verdana\">of</font> <span style=\"background-color: " +
        "red;\">quartz</span><svg/onload=alert(\"XSS\")>";

@Autowired
private Dialogs dialogs;

@Subscribe("msgDialogOnBtn")
public void onMsgDialogOnBtnClick(Button.ClickEvent event) {
    dialogs.createMessageDialog()
            .withCaption("MessageDialog with Sanitizer")
            .withMessage(UNSAFE_HTML)
            .withContentMode(ContentMode.HTML)
            .withHtmlSanitizer(true)
            .show();
}

@Subscribe("msgDialogOffBtn")
public void onMsgDialogOffBtnClick(Button.ClickEvent event) {
    dialogs.createMessageDialog()
            .withCaption("MessageDialog without Sanitizer")
            .withMessage(UNSAFE_HTML)
            .withContentMode(ContentMode.HTML)
            .withHtmlSanitizer(false)
            .show();
}

withHtmlSanitizer() 接收的参数会覆盖全局的 jmix.ui.component.html-sanitizer-enabled 配置。

使用下面的方法可以自定义消息对话框的外观和行为:

  • withModal() - 如果为 false,对话框不以模态窗展示,此时用户可以与应用程序的其他部分交互。对话框默认以模态框展示。

  • withCloseOnClickOutside() - 如果为 true,并且窗口是模态展示时,用户可以点击对话框之外的地方来关闭对话框。

  • withWindowMode() - 设置对话框窗口的模式。有两种预定义模式:

    • WindowMode.NORMAL - 窗口大小和位置根据窗口状态确定。

    • WindowMode.MAXIMIZED - 窗口最大化,左上角对齐后占满整个界面。

  • withStyleName() 可以为对话框设置自定义的 CSS 样式名称。参阅 创建新样式 了解详情。

  • withWidth()withHeight() 支持指定对话框大小。

示例:

@Autowired
private Dialogs dialogs;

@Subscribe("showDialogBtn")
public void onShowDialogBtnClick(Button.ClickEvent event) {
    dialogs.createMessageDialog()
            .withCaption("Information")
            .withMessage("<i>Message<i/>")
            .withContentMode(ContentMode.HTML)
            .withCloseOnClickOutside(true)
            .withWidth("100px")
            .withHeight("300px")
            .show();
}

选项对话框

选项对话框展示消息和一组用户交互的按钮。

option dialog

使用 withActions() 方法可以为对话框提供 操作,每个操作在对话框中以按钮的形式展示。示例:

@Autowired
private Dialogs dialogs;

@Subscribe("optDialogBtn")
public void onOptDialogBtnClick(Button.ClickEvent event) {
    dialogs.createOptionDialog()
            .withCaption("Please confirm")
            .withMessage("Do you really want to add a customer?")
            .withActions(
                    new DialogAction(DialogAction.Type.YES, Action.Status.PRIMARY)
                            .withHandler(e -> doSomething()),
                    new DialogAction(DialogAction.Type.NO)
            )
            .show();
}

当按钮被点击时,对话框会关闭并且调用相应操作的 actionPerform() 方法。

DialogAction 基类可以用来创建带有标准名称和图标的操作。支持五种使用 DialogAction.Type 枚举定义的操作类型:OKCANCELYESNOCLOSE。对应的按钮名称通过主语言消息包获取。

DialogAction 构造器的第二个参数用来为操作的按钮设置特殊的可视化样式。jmix-primary-action 样式提供的 Status.PRIMARY 会高亮对应的按钮并默认选中。如果对话框中有多个操作使用了 Status.PRIMARY,只有第一个操作的按钮能使用正确的样式并获得焦点。

输入对话框

输入对话框是一个多功能的工具,可以使用 API 构建输入表单,摆脱为琐碎的数据输入创建界面的麻烦。支持不同类型数据的输入、验证输入数据以及为用户提供不同的操作。

input dialog

下面我们看几个例子。

标准参数

下面例子中,输入对话框带有标准类型的参数和标准的 OK / Cancel 操作:

@Autowired
private Dialogs dialogs;

@Subscribe("inputDialogBtn")
public void onInputDialogBtnClick(Button.ClickEvent event) {
    dialogs.createInputDialog(this)
            .withCaption("Get values")
            .withParameters(
                    InputParameter.dateTimeParameter("deliveryTime")
                            .withCaption("Delivery Time")
                            .withRequired(true),(1)
                    InputParameter.doubleParameter("amount")
                            .withCaption("Amount")
                            .withDefaultValue(1.0),(2)
                    InputParameter.entityParameter("customer", Customer.class)
                            .withCaption("Customer"),(3)
                    InputParameter.enumParameter("status", Status.class)
                            .withCaption("Status")(4)
            )
            .withActions(DialogActions.OK_CANCEL)(5)
            .withCloseListener(closeEvent -> {
                if (closeEvent.closedWith(DialogOutcome.OK)) {(6)
                    String name = closeEvent.getValue("name");(7)
                    Double quantity = closeEvent.getValue("quantity");
                    Customer customer = closeEvent.getValue("customer");
                    Status status = closeEvent.getValue("status");
                    // process entered values...
                }
            })
            .show();
}
1 指定一个必填的字符串参数。
2 指定一个带有默认值的双浮点参数。
3 指定一个实体参数。
4 指定一个枚举参数。
5 指定一组用按钮表示的操作,放置于对话框底部。
6 在关闭事件监听器中,我们可以检查用户使用了什么操作。
7 关闭事件包含了输入的值,可以通过参数标识符进行获取。

自定义参数

@Autowired
private Dialogs dialogs;

@Autowired
private UiComponents uiComponents;

@Subscribe("inpDlgParamsBtn")
public void onInpDlgParamsBtnClick(Button.ClickEvent event) {
    dialogs.createInputDialog(this)
            .withCaption("Enter some values")
            .withParameters(
                    InputParameter.stringParameter("name").withCaption("Name"),
                    InputParameter.parameter("customer")(1)
                            .withField(() -> {
                                EntityComboBox<Customer> field = uiComponents.create(
                                        EntityComboBox.of(Customer.class));
                                field.setOptionsList(dataManager.load(Customer.class).all().list());
                                field.setCaption("Customer");(2)
                                field.setWidthFull();
                                return field;
                            })
            )
            .withActions(DialogActions.OK_CANCEL)
            .withCloseListener(closeEvent -> {
                if (closeEvent.closedWith(DialogOutcome.OK)) {
                    String name = closeEvent.getValue("name");
                    Customer customer = closeEvent.getValue("customer");(3)
                    // process entered values...
                }
            })
            .show();
}
1 指定一个自定义参数
2 在创建的组件中指定自定义参数的标题。
3 跟标准的参数一样的方法获取自定义参数的值。

自定义操作

@Autowired
private Dialogs dialogs;

@Subscribe("inpDlgActionsBtn")
public void onInpDlgActionsBtnClick(Button.ClickEvent event) {
    dialogs.createInputDialog(this)
            .withCaption("Enter some values")
            .withParameters(
                    InputParameter.stringParameter("name").withCaption("Name")
            )
            .withActions((1)
                    InputDialogAction.action("confirm")
                            .withCaption("Confirm")
                            .withPrimary(true)
                            .withHandler(actionEvent -> {
                                InputDialog dialog = actionEvent.getInputDialog();
                                String name = dialog.getValue("name");(2)
                                dialog.closeWithDefaultAction();(3)
                                // process entered values...
                            }),
                    InputDialogAction.action("refuse")
                            .withCaption("Refuse")
                            .withValidationRequired(false)
                            .withHandler(actionEvent ->
                                    actionEvent.getInputDialog().closeWithDefaultAction())
            )
            .show();
}
1 withActions() 方法能接收一组用户自定义的操作。
2 在操作处理器中,可以从对话框获取参数值。
3 自定义操作不会关闭对话框本身,所以需要同时手动关闭。

自定义校验

@Autowired
private Dialogs dialogs;

@Subscribe("inpDlgValidBtn")
public void onInpDlgValidBtnClick(Button.ClickEvent event) {
    dialogs.createInputDialog(this)
            .withCaption("Enter some values")
            .withParameters(
                    InputParameter.stringParameter("name").withCaption("Name"),
                    InputParameter.entityParameter("customer", Customer.class).withCaption("Customer")
            )
            .withValidator(context -> {(1)
                String name = context.getValue("name");(2)
                Customer customer = context.getValue("customer");
                if (Strings.isNullOrEmpty(name) && customer == null) {
                    return ValidationErrors.of("Enter name or select a customer");
                }
                return ValidationErrors.none();
            })
            .withActions(DialogActions.OK_CANCEL)
            .withCloseListener(closeEvent -> {
                if (closeEvent.closedWith(DialogOutcome.OK)) {
                    String name = closeEvent.getValue("name");
                    Customer customer = closeEvent.getValue("customer");
                    // process entered values...
                }
            })
            .show();
}
1 需要自定义 Validator 确保至少输入了一个参数。
2 在 Validator 中,参数值可以通过上下文对象获取。

捕获异常对话框

这是一个特殊的对话框,用于展示后台异常。对话框包含一个可折叠的区域,展示完整的异常堆栈信息。

exception dialog

此对话框用在默认的处理器中,但是也可以用于自己的异常。示例:

@Autowired
private Dialogs dialogs;

@Subscribe("expDlgBtn")
public void onExpDlgBtnClick(Button.ClickEvent event) {
    try {
        int d = 0;
        int a = 42 / d;
    }
    catch (ArithmeticException e) {
        dialogs.createExceptionDialog()
                .withCaption("Alert")
                .withMessage("Division by zero")
                .withThrowable(e.fillInStackTrace())
                .show();
    }
}