Docs

Form Validation

The Bean Validation constraints that forms check in the browser, validators of your own, and errors that a service reports.

A form validates its value in the browser, so that the user sees errors right away. The service validates the value again on the server, when the form is submitted. The browser runs the Bean Validation constraints of the Java type, and validators that the view adds. The server runs only the Bean Validation constraints, and checks of its own.

The browser validates a field when the user changes its value, and when the field loses focus. On submit, it validates the whole form.

Bean Validation Constraints

The generator copies these Jakarta Bean Validation constraints from the Java type to the form model, so that the browser checks them, too:

  • @AssertFalse, @AssertTrue

  • @DecimalMax, @DecimalMin, @Digits, @Max, @Min

  • @Negative, @NegativeOrZero, @Positive, @PositiveOrZero

  • @Email, @NotBlank, @NotEmpty, @Pattern, @Size

  • @Future, @Past

  • @NotNull, @Null

Only the server checks other constraints, such as @PastOrPresent, @FutureOrPresent, and constraints of your own. Their errors appear in the fields after a submit, as described in Server-Side Validation.

For example, the Person record on Forms has these constraints:

Source code
Person.java

The @Valid annotations make the server validate the nested objects, too. The browser validates them in any case.

Required Fields

A field is required when its property has a @NotNull, @NotEmpty, or @NotBlank constraint, or a @Size constraint with a min greater than 0. The field() function sets the required state of the component, which shows an indicator.

When a field isn’t required and its value is empty, the browser doesn’t run its other validators. For example, an empty postal code passes a @Pattern constraint. A number field is an exception: an empty field for a non-nullable number fails with the message "must be a number".

Error Messages

When a constraint has no message attribute in Java, the browser shows a message of its own in English, such as "must not be blank". After a submit, the field shows the message of the server, which comes from Bean Validation. When the constraint has a message attribute, both use it.

The browser shows the message as it’s written in Java. It doesn’t replace placeholders such as {min}. To replace them, set the static interpolateMessageCallback property of BinderRoot, from @vaadin/hilla-lit-form. It gets the message and the validator, which has the attributes of the constraint as properties:

Source code
TypeScript
import { BinderRoot } from '@vaadin/hilla-lit-form';

BinderRoot.interpolateMessageCallback = (message, validator) =>
  message.replace(/\{(\w+)\}/gu, (placeholder, name: string) =>
    String((validator as unknown as Record<string, unknown>)[name] ?? placeholder),
  );

Validators in the View

A view can add validators of its own, for checks that have no Bean Validation constraint. Only the browser runs these validators, so they don’t protect the data. When the check matters, make the service check it, too.

A validator is an object with a message and a validate() function. The function gets the value, and returns whether it’s valid. It can also return a promise. Add the validators once, in an effect without dependencies.

The examples in this section use this record:

Source code
SignUp.java

Field Validators

To validate a single field, get its part of the form with useFormPart(), and call addValidator():

Source code
sign-up-form.tsx

The field shows the message of the validator when validate() returns false.

The validators of the Bean Validation constraints are also available as classes in @vaadin/hilla-lit-form, with the same names. For example, username.addValidator(new Size({ max: 20, message: 'At most 20 characters' })) adds a @Size check to a field in the browser.

Form Validators

A check that involves several fields belongs to the form. Call addValidator() of the form, and return the fields to mark from validate():

Source code
sign-up-form.tsx

The validate() function of a form validator gets the whole value of the form. It can return these values:

true or an empty array

The value is valid.

An object or an array of objects with a property, and an optional message

Each object marks the field of its property as invalid. The property is the model of the field, or its path, such as 'address.street'. The message of the object replaces the message of the validator.

false

The form is invalid, but no field shows the error. It’s in the errors of the form.

The browser runs the form validators together with the validators of the fields.

Required Validators

A validator with impliesRequired: true makes its field required, like a @NotBlank constraint. The browser then runs the validator also when the field is empty.

Errors from the Service

A service can report validation errors of its own, for example for checks that need data from a database. Throw an EndpointValidationException, with a ValidationErrorData for each error. Its second argument is the path of the field that shows the error:

Source code
SignUpService.java

The form shows the error in the field, like the errors of the Bean Validation constraints:

Source code
sign-up-form.tsx

Updated –