Binding to Vaadin Flow UI controls

Seife Annotation Processor for Vaadin Package
Create Forms and Grids from JPA datamodels by specifying bindings between userinterface elements and the datamodel

With Seife you can generate code for Vaadin forms and Vaadin grids with just a few and clean directives.

During project compilation the annotations on the data model and on the user interface classes are analyzed. A UI binding will be associated with the particular field of an entity and based on the data type code is generated from templates. The declarative binding makes the code readable while the template based approach keeps it maintainable.

In the JPA model, the annotation @SeifeClass is added next to @Entity and each attribute that is bound to a form field or grid is annotated with @SeifeField. By convention form fields have the same name as the entities’ attribute. If this is not feasible it can be specified explicitly, or via the dot-notation a field of a nested object can be referenced.


@Entity
@SeifeClass // just add this for the form binding, no special options needed
public class Customer {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

    /**
     * This field is exposed to the forms via @SeifeField
     */
    @SeifeField
    private String firstName;

    /**
     * The mandatory flag will create a mandatory input field whenever it is bound.
     */
    @SeifeField(mandatory = true)
    private String lastName;

    /**
     * Embedded address, this can also be a regular database reference
     */
    @SeifeField
    @Embedded
    private Address address;
// [..]
}

Simple enough, the user control gets the @SeifeForm(forClass=Model.class) annotation to indicate that it is a form or grid for your datamodel. The standard case is to mark each field with the @SeifeBinding annotation. In this example a Grid control for the customer class is defined.

@SpringComponent
@Scope(SCOPE_PROTOTYPE)
@SeifeForm(forClass = Customer.class, generatorOptions = {"grid.gridClass"})
public class CustomerGrid extends Grid<Customer> {

    /**
     * The recommended way is to name the fields just as in e.g. {@link Customer}, fields are then bound implicitly.
     */
    @SeifeBinding
    private Column<Customer> firstName;

    /**
     * However if necessary it can be explicitly bound via the name.
     */
    @SeifeBinding("lastName")
    private Column<Customer> name;

    /**
     * You can navigate fields with the dot-notation, even if they may contain null and are not mandatory.
     */
    @SeifeBinding("address.street")
    private Column<Customer> street;
// [..]
}

No additional abstraction layer and dependency to another library is required. The licensed version allows to create your own templates and adjust existing templates to your needs.

Your benefit is a clean and consistent code throughout the whole project. If a datatype changes in the future, a template adjustment will be effective for every user interface that is bound to it.
Below is and excerpt of the generated code for the grid;

	protected final void setupColumns() {
		firstName = addColumn(entity -> entity.getFirstName());
		firstName.setKey(COLUMN_FIRST_NAME);
		firstName.setId("firstName");
		name = addColumn(entity -> entity.getLastName());
		name.setKey(COLUMN_LAST_NAME);
		name.setId("name");
		street = addColumn(entity -> Optional.ofNullable(entity.getAddress()).map(p -> p.getStreet()).orElse(null));
		street.setKey(COLUMN_ADDRESS_STREET);
		street.setId("street");
		birthday = addColumn(entity -> entity.getBirthday());
		birthday.setKey(COLUMN_BIRTHDAY);
		birthday.setId("birthday");
		password = addColumn(entity -> entity.getPassword());
		password.setKey(COLUMN_PASSWORD);
		password.setId("password");
		favoriteProductName = addColumn(entity -> Optional.ofNullable(entity.getFavoriteProduct()).map(p -> p.getName()).orElse(null));
		favoriteProductName.setKey(COLUMN_FAVORITE_PRODUCT_NAME);
		favoriteProductName.setId("favoriteProductName");
		premium = addComponentColumn(entity -> {
				Checkbox cb = new Checkbox(entity.isPremium());
				cb.setEnabled(false);
				return cb;
			});
 	}

This makes it possible to define the bindings in a declarative way without loosing the expressiveness of plain code and debug information.
The system is able to automatically add e.g. checkboxes whenever a boolean or java.lang.Boolean datatype occurs in your model and it handles custom type bindings in the same way.

See the working example that is part of the full version or trial-archive available for download.

Further information: Documentation