Blog

Creating Forms with TypeScript and LitElement

By  
Viktor Lukashov
Viktor Lukashov
·
On Aug 20, 2020 3:46:10 PM
·

Form-typekit

Creating data-intensive UIs with forms and data grids can be difficult. Developers need to consider many aspects—business logic, data consistency, usability, accessibility, maintainability—to create a good experience for users and keep the code complexity manageable. That’s why frameworks like Vaadin offer special support for data binding and form validation.

Vaadin 17 introduces a TypeScript API for form binding. It includes the best parts of the tried-and-true Java Binder API: reliable code completion, type safety, flexible validation, and direct access to the backend data. In addition to this, as TypeScript views run in the browser, the new API offers low latency client-side validation that does not require a stable network connection and works offline.

No boilerplate: use the data model you already have

When creating forms, you can use the data types you already have in Java. There is no need to define the same types again in order to use them in TypeScript. Vaadin automatically generates TypeScript types for data entities and form models, based on the Java data model. This happens during the frontend build phase for any Java classes that are used in at least one @Endpoint (either as a return type, or as a type of one of the arguments).

For example, a Java Order entity type can be used directly when creating forms. Both the Order entity type and the OrderModel form model type are generated and can be imported with a single line.

Order.java

public class Order {

    private Customer customer;
    private List<OrderLine> lines;
    private LocalDate dueDate;
    private LocalTime dueTime;
    private String notes;

    // ... constructor, getters, setters, etc
}

 

order-form.ts

import { Binder, field } from '@vaadin/form';

import OrderModel from
  '../generated/com/example/forms/entities/OrderModel';

private binder = new Binder(this, OrderModel);

 

Code completion and type safety in TypeScript

Vaadin forms use typed form models, where each form field is referred to with a named model property, like model.customer.email. This enables reliable code completion and prevents typos and hard-to-detect errors that are typical when using plain strings like 'customer.email'.

image1-2

Note the use of the ... spread operator. Similar to how it works in JavaScript, here it binds several properties and events of the <vaadin-text-field> web component with a single attribute.

Another benefit of this approach is automatic type checking, based on the known model property types. In the example below, the type of the value parameter in the validator is inferred from the known type of the model.customer property.

image3-1

Shared validators for server and client: more secure and easier to maintain

The user input is validated to conform to the data model as early as possible: in many cases this occurs immediately (as the user finishes typing). All validation constraints on the Java data model, such as @NotBlank, @Email and other JSR-380 annotations, are taken into account automatically.

The example below shows a string length constraint on the fullName property of a Customer Java type.

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

public class Customer {

    @NotBlank
    @Size(max = 32)
    private String fullName;

    // ... more properties, constructor, getters, setters, etc
}

 

This constraint is automatically taken into account in any form created using the Vaadin form-binding API in TypeScript.

private binder = new Binder(this, OrderModel);

render() {
  const { model } = this.binder;
  return html`
    <vaadin-text-field label="Customer Name"
      ...="${field(model.customer.fullName)}">
    </vaadin-text-field> 
  `;
}

 

Vaadin Binder automatically applies this constraint, immediately as the user types in the bound field: it is evaluated in the browser, with minimal latency, and without network round trips.

image2-2

Vaadin offers client-side implementations for most of the built-in JSR-380 validators, in order to reduce latency. In addition, all constraints are always re-validated on the server-side to ensure data consistency.

The TypeScript form-binding API is available in Vaadin 17+ applications from the @vaadin/form module. For more information on type safety, generated form models, validation, support for array properties and reacting to form state changes, please check out the docs. And try it for yourself: download a project from start.vaadin.com and see it in action there.

Viktor Lukashov
Viktor Lukashov
After 15+ years as engineer, architect, product manager and trainer Viktor is now building the next generation of developer tools for the StepZen GraphQL platform. When his passion for tech lets him go, you'll find Viktor chasing another passion - rock climbing.
Other posts by Viktor Lukashov