Fondations de Vaadin

Nicolas Frankel

Pourquoi Vaadin ?

La solution standard pour faire du web avec Java est Java EE

Le Web avec Java EE

HTTP

Java

HTML

API Servlet

CSS

JSP

JavaScript

Taglibs/Tag files

AJAX

DOM

Le Web avec Vaadin

ObligatoireFacultatif

Java

CSS

Vaadin

Web Component

Quelques principes

  • Code en Java

    • Compilé en bytecode

  • Basé sur des composants réutilisables

  • Programmation évènementielle

  • Compatibles mobile

Contraintes

  • Java 8+

  • Container de servlets Java EE 6+

Architecture générale

architecture

Concepts fondamentaux

  • Servlet

  • UI

  • Route

  • Composants

  • Containers

Servlet

  • Servlet spécifique VaadinServlet

  • Enregistre automatiquement une instance sous /*

    • Si aucune instance n’est enregistrée explicitement

Vaadin Servlet

The main servlet, which handles all incoming requests to the application.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/server/VaadinServlet.html
— Vaadin Javadocs
vaadin servlet

UI

The topmost component in any component hierarchy. There is one UI for every Vaadin instance in a browser window. A UI may either represent an entire browser window (or tab) or some part of a html page where a Vaadin application is embedded.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/component/UI.html
— Vaadin Javadocs

Caractéristiques

  • Représente le <body> d’une page HTML

  • Classe d’UI spécifiée par une annotation

ui

Exemple de configuration explicite

public class MyUI extends UI {

    @Override
    protected void init(VaadinRequest request) {
        add(new Span("Hello world!"));
    }
}

L’UI doit être explicitement enregistrée, comme le servlet

Component

Component is a higher level abstraction of an Element or a hierarchy of Elements.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/component/Component.html
— Vaadin Javadocs

Caractéristiques

  • Classe (abstraite) de plus haut niveau héritée par tous les composants Vaadin

  • Disposé sur un composant parent

  • Ou annotée avec une annotation @Route

component

Composants disponibles

  • Label

  • TextField

  • Button

  • Select

  • Grid

  • etc.

Samplers

sampler

Has Components

A component to which the user can add and remove child components.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/component/HasComponents.html
— Vaadin Javadocs

Has Components disponibles

  • VerticalLayout

  • HorizontalLayout

  • FormLayout

  • etc.

has components

Route

Defines the route for components that function as navigation targets in routing.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/router/Route.html
— Vaadin Javadocs

Caractéristiques

  • Associe un chemin d’accès à un composant

  • Indiqué par l’annotation @Route

  • Configuré par Vaadin au démarrage de l’application

Chemin par défaut

  • Nom de la classe

  • Suppression du suffixe View

  • En minuscules

  • Ou "" pour la vue MainXXX

routes 1
routes 2

Exemple de route

@Route("home")
public class MainView extends Div {

  public MainView() {
    setText("Hello world!");
  }
}

Routes : concepts avancés

  • Composant affiché

  • Routes paramétrées

  • Liens de routeur

Composant affiché par la route

  • Par défaut, le composant est affiché comme enfant de l’UI

  • Comment faire pour l’afficher sur un autre parent ?

Elément @Route.layout

  • Configure le composant sur lequel le composant doit être affiché

  • Le composant doit implémenter RouterLayout

Exemple de code

@Route(layout = ParentView.class)
public class AView extends VerticalLayout { }

public class ParentView extends Div implements RouterLayout {
}

Parent de parent

  • Si le parent doit lui-même avoir un parent, il existe @ParentLayout

  • Le parent configuré doit également implémenter RouterLayout

Exemple de code

@Route(layout = ParentView.class)
public class AView extends VerticalLayout { }

@ParentLayout(RootLayout.class)
public class ParentView extends Div implements RouterLayout {
}

public class RootLayout extends Div implements RouterLayout {
}

Routes paramétrées

  • Comment gérer les URL de type REST ?

    • Par exemple, person/1

parameterized routes

Exemple de route paramétrée

@Route("person")
public class PersonView extends Div
                        implements HasUrlParameter<String> {
  @Override
  public void setParameter(BeforeEvent event,
                           String person) {
    setText("Hello " + person + "!"); (1)
  }
}
1person/Doe

Exemple de route paramétrée

@Route("person")
public class PersonView extends Div
                        implements HasUrlParameter<String> {
  @Override
  public void setParameter(BeforeEvent event,
        @OptionalParameter String person) {
    if (person == null) {
      setText("Hello nobody!");         (1)
    } else {
      setText("Hello " + person + "!"); (2)
    }
  }
}
1person
2person/Doe

Lien de routeur

Permet de construire un lien vers un composant

router link

Exemple de lien de routeur

new RouterLink("Say hello", HelloView.class, "John");

public class HelloView extends Div
                        implements HasUrlParameter<String> {

  @Override
  public void setParameter(BeforeEvent event,
                           String name) {
    setText("Hello " + name + "!");
  }
}

Programmation Evènementielle

  • Implémentation du pattern Observer

  • Présent dans tous les clients lourds :

    • Swing/AWT

    • SWT

    • Flex

    • JavaFX

  • Mais pas dans le web…​

observer

Qui est Subject ?

  • Tous les Component sont des Subject

  • Génèrent des évènements en fonction :

    • Du type de composant

    • Du type de déclencheur

Exemple

Button génère :

  • FocusEvent

  • BlurEvent

  • ClickEvent

Qui est Observer ?

  • Toute classe peut implémenter Observer

  • Par exemple, l’UI

Côté client

  • Du JavaScript implémente le code serveur

  • Une interaction client-serveur se traduit par un appel AJAX

observer impl
click notifier

Nature des abonnés

  • Notion d’abonnement portée par une interface

  • N’importe laquelle :

    • Une classe anonyme

    • Une classe dédiée

Classe anonyme abonnée

Avantages

Porte l’information du comportement avec sa déclaration

Désavantages
  • Perte de la référence sur le comportement

  • Couplage fort

new Button("Click me!").addClickListener(
  new ComponentEventListener<ClickEvent<Button>>() {

    @Override
    public void onComponentEvent(ClickEvent<Button> e) {
      ...
    }
  }
);

new Button("Click me!").addClickListener(event -> {
  ...
});

Classe dédiée abonnée

Avantages
  • Entièrement orienté objet

  • Lisible

  • Changement dynamique possible

  • Séparation des responsabilités

  • Nommage sémantique

Désavantage

Verbeux

public class SendFormBehavior
  implements ComponentEventListener<ClickEvent<Button>> {

  @Override
  public void onComponentEvent(ClickEvent<Button> e) {
    ...
  }
}

Problématique liées au pattern Observer

Couplage entre l' Observer et son sujet

Event Bus

Transport event occurrences from sources to subscribers, where event sources signal event occurrences to all event subscribers and event subscribers receive event occurrences.
https://en.wikipedia.org/wiki/Event_monitoring
— Wikipedia
eventbus
eventbus annotation
eventbus payload

Implémentations existantes

Exemple de cas d’utilisation

  • Une liste déroulante pour les pays

  • Une liste déroulante pour les villes

    • Uniquement pour les villes du pays

public class CountryComboBox extends ComboBox<Country> {

  public CountryComboBox(EventBus bus) {
    addValueChangeListener(event -> {
      bus.post(new CountryChangedEvent(event.getValue()));
    });
  }
}
public class CityComboBox extends ComboBox<City> {

  @Subscribe
  public void changeCities(CountryChangedEvent event) {
    // Refresh cities
  }
}
@Route
public class SampleView extends VerticalLayout {

  public SampleView() {
    EventBus eventBus = new EventBus();
    CountryComboBox countries = new CountryComboBox(eventBus);
    CityComboBox cities = new CityComboBox();
    eventBus.register(cities);
    add(countries, cities);
  }
}

Has Validation

A component that supports input validation.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/component/HasValidation.html
— Vaadin Javadocs
invalid
has validation

Mettre un composant en erreur

ComboBox combo = new ComboBox();
...
combo.setInvalid(true);
combo.setErrorMessage("Invalid selection");

Error Handler

  • Gère les erreurs inattendues

  • Par exemple, les exceptions

error handler

Par défaut

Log l’exception dans la sortie d’erreur

Notifications

Informe l’utilisateur par l’affichage d’un composant contextuel

error notification
notification

Has Validator

A generic interface for field components and other user interface objects that have a user-editable value that should be validated.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/data/binder/HasValidator.html
— Vaadin Javadocs
validator

Validateurs disponibles

  • XXXRangeValidator :

    • Types wrapper numériques

    • BigInteger & BigDecimal

    • Date & DateTime

  • BeanValidator

  • RegexpValidator

  • EmailValidator

  • StringLengthValidator

Exemple d’utilisation

public class EmailField extends TextField implements HasValidator<String> {

  public EmailField() {
    addValueChangeListener(event -> {
      ValidationResult r = getDefaultValidator().apply(getValue(), null);
      if (r.isError()) { setToInvalid(r.getErrorMessage()); }
      else { resetToValid(); }
    });
  }

  @Override
  public Validator<String> getDefaultValidator() {
    return new EmailValidator("Value " + getValue() + " is not an email");
  }
}

Data Binding

  • Gestion évènementielle du changement d’état du modèle vers un composant graphique

  • Et vice-versa

Niveaux de data binding

  • Propriété p.e. email

  • Bean p.e Person

  • Liste de beans

Has Value

A generic interface for field components and other user interface objects that have a user-editable value. Emits change events whenever the value is changed, either by the user or programmatically.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/component/HasValue.html
— Vaadin Javadocs
has value

Binder

Connects one or more Field components to properties of a backing data type such as a bean type.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/data/binder/Binder.html
— Vaadin documentation
binder

Exemple

Binder<Person> binder = new Binder<>(Person.class);
binder.setBean(new Person());
TextField firstNameField = new TextField("Given name");
TextField lastNameField = new TextField("Family name");
DatePicker birthdateField = new DatePicker("Date of birth");
binder.bind(firstNameField, "firstName");
binder.bind(lastNameField, "lastName");
binder.bind(birthdateField, "birthdate");

// User interaction

Person person = binder.getBean();
String firstName = person.getFirstName();
String lastName = person.getLastName();
LocalDate birthdate = person.getBirthdate();
form binder

Has Items

Mixin interface for components that displays a collection of items.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/data/binder/HasItems.html
— Vaadin Javadocs
has items

Exemple de code

List<Person> persons = ...;
ListDataProvider<Person> provider =
    new ListDataProvider<>(persons);
Grid<Person> grid = new Grid<>(Person.class);
grid.setDataProvider(provider);
grid.setColumns("firstName", "lastName", "birthdate");
has items
data providers

Grid

Server-side component for the <vaadin-grid> element.
https://vaadin.com/api/platform/14.0.0/com/vaadin/flow/component/grid/Grid.html
— Vaadin Javadocs
grid

Affichage des colonnes

  • Indique les colonnes visibles

  • Mais également leur ordre

Grid<Person> grid = new Grid<>(Person.class);
grid.setColumns("firstName", "lastName");

Libellé de l’en-tête de colonne

Par défaut, inféré du nom de l’attribut

Grid<Person> grid = new Grid<>(Person.class);
grid.getColumn("firstName")
    .setHeader("Prénom");

Libellé de la cellule

  • Récupère la valeur de l’attribut

  • Utilise le Renderer pour la transformer en String

  • Affiche la String dans la cellule

Exemple : formatage de date

Grid<Person> grid = new Grid<>(Person.class);
grid.addColumn(
    new LocalDateRenderer<>(Person::getBirthdate, "")
);

Exemple : ajout de bouton

grid.addColumn(new NativeButtonRenderer<>(
    "Click me!",
    event -> Notification.show("Hello world!"))
);
grid