3.5.6. Диалоговые окна
Интерфейс Dialogs
предназначен для отображения стандартных диалоговых окон. Его методы createMessageDialog()
, createOptionDialog()
и createInputDialog()
являются точками входа в fluent API, позволяющий конструировать и отображать диалоги.
Внешний вид диалоговых окон можно настроить с помощью переменных SCSS с префиксом $cuba-window-modal-*
. Эти переменные можно изменить в визуальном редакторе после расширения темы или создания новой темы.
- Message Dialog
-
В примере ниже диалог отображает сообщение при нажатии кнопки:
@Inject private Dialogs dialogs; @Subscribe("showDialogBtn") protected void onShowDialogBtnClick(Button.ClickEvent event) { dialogs.createMessageDialog().withCaption("Information").withMessage("Message").show(); }
С помощью метода
withMessage()
можно передавать текст сообщения.В тексте можно использовать символы
\n
для перевода строк. Для отображения HTML необходимо передатьContentMode.HTML
в методwithContentMode()
. При использовании HTML обязательно экранируйте данные, полученные из БД, во избежание инжекции вредоносного кода.Вы можете передать значение
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\")>"; @Inject private Dialogs dialogs; @Subscribe("showMessageDialogOnBtn") public void onShowMessageDialogOnBtnClick(Button.ClickEvent event) { dialogs.createMessageDialog() .withCaption("MessageDialog with Sanitizer") .withMessage(UNSAFE_HTML) .withContentMode(ContentMode.HTML) .withHtmlSanitizer(true) .show(); } @Subscribe("showMessageDialogOffBtn") public void onShowMessageDialogOffBtnClick(Button.ClickEvent event) { dialogs.createMessageDialog() .withCaption("MessageDialog without Sanitizer") .withMessage(UNSAFE_HTML) .withContentMode(ContentMode.HTML) .withHtmlSanitizer(false) .show(); }
Значение, переданное в метод
withHtmlSanitizer()
, имеет приоритет над значением глобального свойства cuba.web.htmlSanitizerEnabled.Следующие методы позволяют изменить параметры отображения и поведения диалога:
-
withModal()
– если переданоfalse
, диалог отображается как немодальный, что позволяет пользователю взаимодействовать с другими частями приложения.
-
withCloseOnClickOutside()
– если переданоtrue
и диалог модальный, то пользователь может закрыть диалог, щелкнув на любой части окна приложения вне диалога.
-
withMaximized()
– если переданоtrue
, диалог будет развёрнут во весь экран.
-
withWidth()
,withHeight()
позволяют указать желаемую геометрию диалога.
Например:
@Inject private Dialogs dialogs; @Subscribe("showDialogBtn") protected 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()
массив действий, для каждого из которых в диалоге создается кнопка. Например:@Inject private Dialogs dialogs; @Subscribe("showDialogBtn") protected void onShowDialogBtnClick(Button.ClickEvent event) { dialogs.createOptionDialog() .withCaption("Confirm") .withMessage("Are you sure?") .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
подсвечивает кнопку и задаёт ей выделение по умолчанию, что обеспечивается стилемc-primary-action
. Если для диалога задано несколько действий сStatus.PRIMARY
, то фокус и стиль получает только кнопка первого такого действия в списке.
- Input Dialog
-
Диалог ввода - это мощный инструмент, позволяющий конструировать формы ввода с помощью API, который часто может избавить от необходимости создавать экраны для тривиального ввода данных. Он позволяет вводить значения разнообразных типов, валидировать их и предоставлять различные действия для выбора пользователем.
Рассмотрим несколько примеров.
-
Диалог ввода с параметрами стандартных типов и стандартными действиями OK/Cancel:
@Inject private Dialogs dialogs; @Subscribe("showDialogBtn") private void onShowDialogBtnClick(Button.ClickEvent event) { dialogs.createInputDialog(this) .withCaption("Enter some values") .withParameters( InputParameter.stringParameter("name") .withCaption("Name").withRequired(true), (1) InputParameter.doubleParameter("quantity") .withCaption("Quantity").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"); Optional<Customer> customer = closeEvent.getOptional("customer"); (8) Status status = closeEvent.getValue("status"); // process entered values... } }) .show(); }
1 - задает строковый обязательный параметр. 2 - задает числовой параметр со значением по умолчанию. 3 - задает параметр типа сущность. 4 - задает параметр типа перечисление. 5 - задает набор действий, представляемых кнопками внизу диалога. 6 - в слушателе на закрытие можно определить, какое действие было выбрано пользователем. 7 - событие закрытия содержит введенные значения, которые можно получить по идентификаторам параметров. 8 - можно получить значение, завернутое в Optional
. -
Диалог ввода с нестандартным параметром:
@Inject private Dialogs dialogs; @Inject private UiComponents uiComponents; @Subscribe("showDialogBtn") private void onShowDialogBtnClick(Button.ClickEvent event) { dialogs.createInputDialog(this) .withCaption("Enter some values") .withParameters( InputParameter.stringParameter("name").withCaption("Name"), InputParameter.parameter("customer") (1) .withField(() -> { LookupField<Customer> field = uiComponents.create( LookupField.of(Customer.class)); field.setOptionsList(dataManager.load(Customer.class).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 - значение нестандартного параметра получается таким же способом, как и стандартного. -
Диалог ввода с нестандартными действиями:
@Inject private Dialogs dialogs; @Subscribe("showDialogBtn") private void onShowDialogBtnClick(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 - кастомное действие не закрывает диалог само, поэтому это надо сделать в какой-то момент явно. -
Диалог ввода с валидатором:
@Inject private Dialogs dialogs; @Subscribe("showDialogBtn") private void onShowDialogBtnClick(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 - значения параметров в валидаторе можно получить через объект контекста. -
Диалог ввода с параметром типа
FileDescriptor
:@Inject private Dialogs dialogs; @Subscribe("showDialogBtn") public void onShowDialogBtnClick(Button.ClickEvent event) { dialogs.createInputDialog(this) .withCaption("Select the file") .withParameters( InputParameter.fileParameter("fileField") (1) .withCaption("File")) .withCloseListener(closeEvent -> { if (closeEvent.closedWith(DialogOutcome.OK)) { FileDescriptor fileDescriptor = closeEvent.getValue("fileField"); (2) } }) .show(); }
1 - задает параметр типа FileDescriptor
.2 - событие закрытия содержит введенное значение, которое можно получить по идентификатору параметра.
-