3.5.1.7. 界面中的验证

ScreenValidation bean 可以用来运行界面中的验证逻辑,有如下方法:

  • ValidationErrors validateUiComponents(),默认在 StandardEditorInputDialogMasterDetailScreen 提交改动时使用。该方法接收一组界面组件或者一个组件容器作为参数,返回这些组件中的验证错误(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 对象作为参数。默认在 StandardEditorInputDialogMasterDetailScreen 中使用。

  • validateCrossFieldRules() - 接收界面和实体作为参数,返回 ValidationErrors 对象。执行跨字段验证规则。如果编辑界面的约束中包含 UiCrossFieldChecks 并且所有的属性级别验证通过,则会在提交时,进行类级别约束的验证(更多信息参考 自定义约束 章节)。可以使用控制器的 setCrossFieldValidate() 方法禁用此类验证。默认情况下,在 StandardEditorMasterDetailScreen 以及 DataGrid编辑界面使用。另外,validateCrossFieldRules() 方法也可以用在任意界面中。

    举个例子,对于 Event 实体,我们可以定义类级别注解 @EventDate 来检查 Start date 必须小于 End date

    Event entity
    @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() - 为没保存的数据显示标准对话框("Do you want to discard unsaved changes?" - “您要放弃更改吗?”),带有 Yes - 是No - 否 按钮。用在 StandardEditor 中。showUnsavedChangesDialog() 带有处理用户操作(点击的按钮)的处理器:

    screenValidation.showUnsavedChangesDialog(this, action)
                            .onDiscard(() -> result.resume(closeWithDiscard()))
                            .onCancel(result::fail);
  • showSaveConfirmationDialog() - 为确认保存更改的数据显示标准对话框("Do you want to save changes before close?" - “关闭页面前需要保存修改吗?”)带有 Save - 保存Do not save - 不保存Cancel - 取消 按钮。用在 StandardEditor 中。showSaveConfirmationDialog() 带有处理用户操作(点击的按钮)的处理器:

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

可以用 cuba.gui.useSaveConfirmation 应用程序属性修改对话框类型。