3.5.1.7. Screen Validation
The ScreenValidation
bean can be used to run validation in screens. It has the following methods:
-
ValidationErrors validateUiComponents()
is used by default when committing changes in theStandardEditor
,InputDialog
, andMasterDetailScreen
. The method accepts a collection of components or a component container and returns validation errors in these components (ValidationErrors
object). ThevalidateUiComponents()
method also can be used in an arbitrary screen. For example:@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()
- displays a notification with all errors and problematic components. The method accepts the screen andValidationErrors
object. It is also used by default in theStandardEditor
,InputDialog
, andMasterDetailScreen
. -
validateCrossFieldRules()
- accepts a screen and an entity and returns theValidationErrors
object. Performs cross-field validation rules. Editor screens validate class-level constraints on the commit if the constraints include theUiCrossFieldChecks
group and all attribute-level constraint checks are successful (see more information in the Custom Constraints section). You can disable this type of validation using thesetCrossFieldValidate()
method of the controller. By default, it is used in theStandardEditor
,MasterDetailScreen
, in the editor of theDataGrid
. ThevalidateCrossFieldRules()
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.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; ... }
The annotation definition looks like this:
@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 {}; }
EventDateValidatorpublic 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("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()
- shows the standard dialog for unsaved changes ("Do you want to discard unsaved changes?") with the Yes and No buttons. It is used in theStandardEditor
. TheshowUnsavedChangesDialog()
method has a handler that responds to user actions (the button that was clicked):screenValidation.showUnsavedChangesDialog(this, action) .onDiscard(() -> result.resume(closeWithDiscard())) .onCancel(result::fail);
-
showSaveConfirmationDialog()
- shows the standard dialog for confirming saving changed data ("Do you want to save changes before close?") with the Save, Do not save, Cancel buttons. It is used in theStandardEditor
. TheshowSaveConfirmationDialog()
method has a handler that responds to user actions (the button that was clicked):screenValidation.showSaveConfirmationDialog(this, action) .onCommit(() -> result.resume(closeWithCommit())) .onDiscard(() -> result.resume(closeWithDiscard())) .onCancel(result::fail);
You can adjust the dialog type using the cuba.gui.useSaveConfirmation application property.