3.5.1.7. Валидация в экранах

Бин ScreenValidation может использоваться для запуска валидации в экранах. Он имеет следующие методы:

  • ValidationErrors validateUiComponents() - используется при коммите изменений в экранах StandardEditor, InputDialog, и MasterDetailScreen. Принимает на вход коллекцию из компонентов или контейнер компонентов и возвращает ошибки валидации в этих компонентах (объект ValidationErrors). Метод validateUiComponents() также может быть использован в произвольном экране. Например:

    @UiController("demo_DemoScreen")
    @UiDescriptor("demo-screen.xml")
    public class DemoScreen extends Screen {
        @Inject
        private ScreenValidation screenValidation;
        @Inject
        private Form demoForm;
    
        @Subscribe("validateBtn")
        public void onValidateBtnClick(Button.ClickEvent event) {
            ValidationErrors errors = screenValidation.validateUiComponents(demoForm);
            if (!errors.isEmpty()) {
                screenValidation.showValidationErrors(this, errors);
                return;
            }
        }
    }
  • showValidationErrors() - показывает нотификацию со всеми ошибками и проблемными компонентами. Метод принимает на вход экран и объект ValidationErrors. Также используется по умолчанию в экранах StandardEditor, InputDialog, и MasterDetailScreen.

  • validateCrossFieldRules() - принимает на вход экран и сущность и возвращает объект ValidationErrors. Выполняет правила перекрестной проверки, установленные на поля сущности. Экраны редактирования выполняют валидацию ограничений уровня класса при коммите, если ограничения включают группу UiCrossFieldChecks, и все проверки ограничений уровня атрибутов прошли успешно (больше информации см. в разделе Собственные ограничения). Валидацию данного типа можно отключить с помощью метода контроллера setCrossFieldValidate(). По умолчанию используется в экранах StandardEditor, MasterDetailScreen, в редакторе DataGrid. Метод validateCrossFieldRules() также может быть использован в произвольном экране.

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

    Сущность Event
    @Table(name = "DEMO_EVENT")
    @Entity(name = "demo_Event")
    @NamePattern("%s|name")
    @EventDate(groups = {Default.class, UiCrossFieldChecks.class})
    public class Event extends StandardEntity {
        private static final long serialVersionUID = 1477125422077150455L;
    
        @Column(name = "NAME")
        private String name;
    
        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "START_DATE")
        private Date startDate;
    
        @Temporal(TemporalType.TIMESTAMP)
        @Column(name = "END_DATE")
        private Date endDate;
    
        ...
    }

    Определение аннотации выглядит следующим образом:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = EventDateValidator.class)
    public @interface EventDate {
    
        String message() default "The Start date must be earlier than the End date";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }
    EventDateValidator
    public class EventDateValidator implements ConstraintValidator<EventDate, Event> {
        @Override
        public boolean isValid(Event event, ConstraintValidatorContext context) {
            if (event == null) {
                return false;
            }
    
            if (event.getStartDate() == null || event.getEndDate() == null) {
                return false;
            }
    
            return event.getStartDate().before(event.getEndDate());
        }
    }

    Далее вы можете использовать метод validateCrossFieldRules() в произвольном экране.

    @UiController("demo_DemoScreen")
    @UiDescriptor("demo-screen.xml")
    public class DemoScreen extends Screen {
    
        @Inject
        protected Metadata metadata;
        @Inject
        protected ScreenValidation screenValidation;
        @Inject
        protected TimeSource timeSource;
    
        @Subscribe("validateBtn")
        public void onValidateBtnClick(Button.ClickEvent event) {
            Event event = metadata.create(Event.class);
            event.setName("Demo event");
            event.setStartDate(timeSource.currentTimestamp());
    
            // We make the endDate earlier than the startDate
            event.setEndDate(DateUtils.addDays(event.getStartDate(), -1));
    
            ValidationErrors errors = screenValidation.validateCrossFieldRules(this, event);
            if (!errors.isEmpty()) {
                screenValidation.showValidationErrors(this, errors);
            }
        }
    }
  • showUnsavedChangesDialog() - показывает стандартный диалог несохраненных изменений ("Вы действительно хотите закрыть экран?") с кнопками Да и Нет. Используется в экране редактора StandardEditor. Метод showUnsavedChangesDialog() имеет обработчик, который реагирует на действия пользователя (кнопку, которую он нажал):

    screenValidation.showUnsavedChangesDialog(this, action)
                            .onDiscard(() -> result.resume(closeWithDiscard()))
                            .onCancel(result::fail);
  • showSaveConfirmationDialog() - показывает стандартный диалог подтверждения сохранения измененных данных ("Сохранить изменения перед закрытием экрана?") с кнопками Сохранить, Не сохранять, Отмена. Используется в экране редактора StandardEditor. Метод showSaveConfirmationDialog() имеет обработчик, который реагирует на действия пользователя (кнопку, которую он нажал):

    screenValidation.showSaveConfirmationDialog(this, action)
                        .onCommit(() -> result.resume(closeWithCommit()))
                        .onDiscard(() -> result.resume(closeWithDiscard()))
                        .onCancel(result::fail);

Вы можете настроить тип диалога с помощью свойства приложения cuba.gui.useSaveConfirmation.