Screen Validation

The ScreenValidation bean is used to run validation in screens. It has the following methods:

  • validateUiComponents() is used by default when committing changes in the StandardEditor, InputDialog, and MasterDetailScreen. The method accepts a collection of components or a component container and returns validation errors in these components - ValidationErrors object. The validateUiComponents() method also can be used in an arbitrary screen. For example:

    @UiController("sample_DemoScreen")
    @UiDescriptor("demo-screen.xml")
    public class DemoScreen extends Screen {
    
        @Autowired
        private ScreenValidation screenValidation;
    
        @Autowired
        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() - displays a notification with all errors and problematic components. The method accepts the screen and ValidationErrors object. It is also used by default in the StandardEditor, InputDialog, and MasterDetailScreen.

  • validateCrossFieldRules() - accepts a screen and an entity and returns the ValidationErrors object. By default, it is used in the StandardEditor, MasterDetailScreen, and in the editor of the DataGrid. This method performs cross-field validation rules.

    Editor screens validate class-level constraints on the commit if the constraints include the UiCrossFieldChecks group and all attribute-level constraint checks are successful. You can disable this type of validation using the setCrossFieldValidate() method of the controller. The validateCrossFieldRules() method also can be used in an arbitrary screen.

    As an example, let’s look at the Event entity for which we can define a class-level annotation @EventDate to check that the start date must be lower than the end date:

    @JmixEntity
    @Table(name = "SAMPLE_EVENT")
    @Entity(name = "sample_Event")
    @EventDate(groups = {Default.class, UiCrossFieldChecks.class})
    public class Event {
        @JmixGeneratedValue
        @Column(name = "ID", nullable = false)
        @Id
        private UUID id;
    
        @Column(name = "NAME")
        @InstanceName
        private String name;
    
        @Column(name = "START_DATE")
        @Temporal(TemporalType.TIMESTAMP)
        private Date startDate;
    
        @Column(name = "END_DATE")
        @Temporal(TemporalType.TIMESTAMP)
        private Date endDate;
    
        // ...

    The annotation definition looks like this:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = EventDateValidator.class)
    public @interface EventDate {
    
        String message() default "Start date must be earlier than the end date";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }

    Here is the EventDateValidator class:

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

    Then you can use the validateCrossFieldRules() method in an arbitrary screen.

    @UiController("sample_DemoScreen")
    @UiDescriptor("demo-screen.xml")
    public class DemoScreen extends Screen {
    
        @Autowired
        private ScreenValidation screenValidation;
    
        @Autowired
        protected Metadata metadata;
    
        @Autowired
        protected TimeSource timeSource;
    
        @Subscribe("validateDateBtn")
        public void onValidateDateBtnClick(Button.ClickEvent event) {
            Event demoEvent = metadata.create(Event.class);
            demoEvent.setName("Demo event");
            demoEvent.setStartDate(timeSource.currentTimestamp());
            demoEvent.setEndDate(DateUtils.addDays(demoEvent.getStartDate(), -1));
            ValidationErrors errors = screenValidation.validateCrossFieldRules(this, demoEvent);
            if (!errors.isEmpty()) {
                screenValidation.showValidationErrors(this, errors);
            }
        }
    }
  • showUnsavedChangesDialog() - shows the standard dialog for unsaved changes with Yes and No buttons. It is used in the StandardEditor. The showUnsavedChangesDialog() method has a handler that responds to user actions. You can see an example of usage in the io.jmix.ui.screen.StandardEditor#preventUnsavedChanges method.

  • showSaveConfirmationDialog() - shows the standard dialog for confirming saving changed data with Save, Do not save, and Cancel buttons. It is used in the StandardEditor. The showSaveConfirmationDialog() method has a handler that responds to user actions. You can see an example of usage in the io.jmix.ui.screen.StandardEditor#preventUnsavedChanges method.

    You can adjust the dialog type using jmix.ui.screen.useSaveConfirmation application property.