Диалоговые окна

Интерфейс Dialogs предназначен для отображения стандартных диалоговых окон. Диалоги – это небольшие окна, используемые для отображения информации и элементов UI поверх остального интерфейса.

Его методы createMessageDialog(), createOptionDialog() и createInputDialog() являются точками входа в fluent API, позволяющий конструировать и отображать диалоги.

Внешний вид диалоговых окон можно настроить с помощью переменных SCSS с префиксом $jmix-window-modal-*. Эти переменные можно изменить в визуальном редакторе после создания новой темы.

Диалог сообщения

Самый простой вариант использования диалогового окна – это показ пользователю некоторого сообщения.

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 необходимо передать параметр ContentMode.HTML в метод withContentMode().

Вы можете передать значение true в метод withHtmlSanitizer(), чтобы включить HTML санитизацию для содержимого диалога. Также в этом случае параметр ContentMode.HTML должен быть передан в метод withContentMode().

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: OK, CANCEL, YES, NO, CLOSE. Названия соответствующих кнопок извлекаются из главного пакета локализованных сообщений.

Второй параметр конструктора DialogAction используется для задания визуального стиля кнопки, к которой привязано данное действие. Статус Status.PRIMARY подсвечивает кнопку и задает ей выделение по умолчанию, что обеспечивается стилем jmix-primary-action. Если для диалога задано несколько действий с 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 Нестандартный валидатор в данном примере необходим для того, чтобы обеспечить ввод как минимум одного параметра.
2 Значения параметров в валидаторе можно получить через объект контекста.

Диалог исключения

Это специальный диалог для отображения исключений, содержащий сворачиваемую область с полным стектрейсом исключения.

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();
    }
}