Skip to content
Version:

Form

Die Form umschließt ein komplettes Formular und verantwortet dessen formularweite Zustände.

Overview

Nicht das Vaadin Form Layout verwenden

Für den Mate-Formularaufbau werden weder <vaadin-form-layout> noch Vaadins FormLayout (com.vaadin.flow.component.formlayout.FormLayout) verwendet.
Verwendet werden <mate-form> als äußerer Formular-Container und <div theme="form-layout"> für die einzelnen Feldergruppen. In Flow ist FormLayout die Klasse de.mate_ds.flow.component.flexlayout.FormLayout.

Intro

Die Form ist der übergreifende Container eines Formulars. Sie hält alle Formularbereiche zusammen und verantwortet die Zustände, die für das gesamte Formular gelten: valide oder nicht valide, die allgemeine Validierung und die Fehlerliste am Anfang des Formulars („Errors on Top“).

Eine Form kann mehrere Form Layouts enthalten. Jedes Form Layout bildet dabei eine logische Feldergruppe, zum Beispiel Personendaten, Adresse oder Bankverbindung. Die Form selbst stellt ihre Kinder untereinander. Sollen Feldergruppen nebeneinander stehen oder um einen Callout als Kontextinformation ergänzt werden, kommt dafür ein Block Layout innerhalb der Form zum Einsatz.

Die Form trifft keine Aussage über die Anordnung einzelner Eingabeelemente. Diese Aufgabe übernimmt das Form Layout. Fachliche Regeln zu Eingabetypen, Pflichtfeldern und Validierung sind im Muster Formulare beschrieben.

Verwendung

👍 Do👎 Don't
...wenn ein Formular als Ganzes erfasst, geprüft und abgeschickt wird und dafür ein gemeinsamer Rahmen benötigt wird.
...wenn mehrere Feldergruppen fachlich zu einem Formular gehören.
...wenn formularweite Fehler gesammelt am Anfang des Formulars ausgegeben werden sollen.
...wenn nur Eingabeelemente innerhalb einer Feldergruppe angeordnet werden sollen. Verwende hierfür das Form Layout.
...wenn große Inhaltsbereiche einer Seite nebeneinander gestellt werden sollen. Verwende hierfür das Block Layout.
...wenn ein Hinweis oder eine Kontextinformation hervorgehoben werden soll. Verwende hierfür den Callout.
...wenn einzelne Eingabeelemente ohne gemeinsamen Formularzustand auf einer Seite stehen. Dafür wird kein Formular-Container benötigt.

Guidelines

Aufbau

Die Form ist die äußerste Ebene des Formulars. Sie ist ein Flex-Container in Spaltenrichtung und stellt ihre Kinder untereinander. Welche Ebenen darunter liegen, hängt davon ab, ob Bereiche nebeneinander stehen sollen.

Ein einspaltiges Formular kommt ohne Block Layout aus. Zwischen der Form und den Feldergruppen liegt dann ein vertikal stapelndes Layout, das für den Abstand zwischen ihnen sorgt:

text
Form
└─ Vertical Layout mit Spacing
   ├─ Form Layout „Person“
   └─ Form Layout „Adresse“

<mate-form> setzt selbst keinen Abstand zwischen seinen Bereichen. Die Theme-CSS enthält für mate-form, ::part(top) und ::part(form-content) keine Regel für gap, row-gap oder margin; eine Größendatei size/base/form.css existiert gar nicht. Zwei Form Layouts direkt untereinander in einer Form stehen deshalb ohne Abstand aneinander. Der Abstand entsteht über das Layout innerhalb der Form. Im einspaltigen Fall ist das ein Vertical Layout mit theme="spacing":

html
<mate-form>
  <vaadin-vertical-layout theme="spacing">
    <div theme="form-layout spacing">
      <h5>Person</h5>
      <vaadin-text-field label="Vorname"></vaadin-text-field>
      <vaadin-text-field label="Nachname"></vaadin-text-field>
    </div>
    <div theme="form-layout spacing">
      <h5>Adresse</h5>
      <vaadin-text-field label="Straße" cols="3"></vaadin-text-field>
      <vaadin-text-field label="Hausnummer" cols="1"></vaadin-text-field>
    </div>
  </vaadin-vertical-layout>
</mate-form>

Dieses Beispiel setzt @mate/theme, @mate/form, @vaadin/vertical-layout und @vaadin/text-field voraus. Die Theme-Datei size/base/flex-layout.css erfasst vaadin-vertical-layout mit und setzt für theme="spacing" den Wert gap: var(--mate-spacing-margin), also 16 Pixel auf dem Desktop und 20 Pixel auf mobilen Geräten. Stehen die Bereiche nebeneinander, übernimmt das Block Layout diese Aufgabe, ebenfalls über theme="spacing": Die Theme-Datei size/base/block-layout.css setzt dafür row-gap und column-gap auf denselben Wert var(--mate-spacing-margin).

Kein layout-12 für den einspaltigen Fall

Ein Block Layout mit theme="layout-12" ist kein Ersatz für das Vertical Layout. Diese Konfiguration blendet ab dem zweiten Kind alles per display: none aus; die Regel dazu steht in der Theme-Datei base/block-layout.css und greift ab dem zweiten Kind-Element.
Ein einspaltiges Formular mit mehreren Feldergruppen würde damit alle Bereiche außer dem ersten verlieren. Für ein einspaltiges Formular wird deshalb ein Vertical Layout mit theme="spacing" verwendet, kein Block Layout.

Ein Block Layout kommt hinzu, sobald mehrere Bereiche nebeneinander stehen sollen, zum Beispiel zwei Feldergruppen und ein Callout als Kontextinformation:

text
Form
└─ Block Layout
   ├─ Form Layout „Person“
   ├─ Form Layout „Adresse“
   └─ Callout „Hinweis“

Ein Block Layout ordnet dabei ausschließlich die Bereiche eines Formulars an, niemals einzelne Eingabefelder. Das folgende Beispiel zeigt diesen Aufbau vollständig:

html
<mate-form>
  <div class="block-container">
    <div theme="block-layout spacing">
      <div theme="form-layout spacing">
        <h5>Person</h5>
        <vaadin-text-field label="Vorname"></vaadin-text-field>
        <vaadin-text-field label="Nachname"></vaadin-text-field>
        <vaadin-text-field label="Personalnummer"></vaadin-text-field>
        <vaadin-date-picker label="Einstellungsdatum"></vaadin-date-picker>
      </div>
      <div theme="form-layout spacing">
        <h5>Adresse</h5>
        <vaadin-text-field label="Straße" cols="3"></vaadin-text-field>
        <vaadin-text-field label="Hausnummer" cols="1"></vaadin-text-field>
        <vaadin-text-field label="Postleitzahl"></vaadin-text-field>
        <vaadin-text-field label="Ort"></vaadin-text-field>
      </div>
      <mate-callout theme="info" title-text="Hinweis">
        Erfassen Sie hier die aktuellen Personal- und Adressdaten der Person.
      </mate-callout>
    </div>
  </div>
</mate-form>

Dieses Beispiel setzt @mate/theme, @mate/form und @mate/callout voraus, dazu @vaadin/text-field und @vaadin/date-picker samt der zugehörigen Importe. Ohne Import bleiben nur die reinen Theme-Layouts nutzbar, also block-layout, form-layout und floating-layout.

Info für Dev

Das Block Layout benötigt den umschließenden Container mit der Klasse block-container. Ohne diesen Container greifen die Container Queries des Block Layouts nicht und die Bereiche bleiben in der kleinsten Konfiguration untereinander.
Das Block Layout zeigt außerdem nur eine begrenzte Zahl an Kindern an: In der Standardkonfiguration sind es drei, überzählige Kinder werden per display: none ausgeblendet. Das Beispiel oben liegt mit zwei Feldergruppen und einem Callout genau auf dieser Grenze. Weitere Bereiche gehören in ein zusätzliches Block Layout. Die Konfigurationen und ihre Grenzen stehen auf der Seite Block Layout.

Die Konfigurationen des Block Layouts und die Zahl der jeweils sichtbaren Bereiche sind beim Block Layout beschrieben.

Zwei Schreibweisen für das Form Layout

Diese Seite verwendet die Schreibweise <div theme="form-layout">. Das am Ende der Seite angehängte Beispiel verwendet dagegen <mate-form-layout>.
Die Theme-CSS erfasst beide Schreibweisen mit demselben Regelsatz. Ein registriertes Custom Element mate-form-layout existiert im Repository jedoch nicht, deshalb ist <div theme="form-layout"> die verlässliche Schreibweise. Das angehängte Beispiel setzt die Fehlerliste zudem über die Eigenschaft erroritems; die Web Component wertet diese Eigenschaft nicht aus, die Meldungen gehören in den Slot error-message. Die Vereinheitlichung der Beispieldateien ist ein offener Punkt.

Rollen und Grenzen

Form, Form Layout, Block Layout und Callout haben klar getrennte Aufgaben.

KomponenteZuständigkeitNicht dafür da
FormFormularweite Zustände, Fehlerliste am Anfang des FormularsAnordnung einzelner Eingabeelemente
Form LayoutEingabeelemente einer FeldergruppeRahmen für ein ganzes Formular
Block LayoutBereiche eines Formulars nebeneinanderAnordnung einzelner Eingabefelder
CalloutKontextinformation hervorhebenAufnahme von Eingabefeldern

Diese Abgrenzung ist eine Mate-Regel für den Aufbau von Formularen, kein erzwungenes Komponentenverhalten. Technisch lassen sich Eingabefelder auch direkt in ein Block Layout oder in einen Callout setzen. Die Einhaltung der Regel liegt in der Verantwortung der Anwendung.

Formularzustände und Fehleranzeige

Die Form kennt einen formularweiten State Error. Er wird über die Eigenschaft invalid gesetzt und als Attribut am Element gespiegelt. Solange invalid nicht gesetzt ist, bleibt die Fehleranzeige vollständig ausgeblendet.

Die Fehleranzeige selbst ist ein fest eingebautes Callout in der Fehlervariante. Es steht am Anfang der Form und setzt damit das Prinzip „Errors on Top“ um: Nach einer fehlgeschlagenen Validierung erscheint die Zusammenfassung aller Fehler oberhalb der Feldergruppen, bevor der Benutzer zu den einzelnen Feldern scrollt.

Diese Bausteine stellt die Komponente bereit:

BausteinBeschreibung
invalidBoolesche Eigenschaft. Sie markiert das gesamte Formular als nicht valide und blendet die eingebaute Fehleranzeige ein.
error-titleTitel der Fehlerzusammenfassung, zum Beispiel eine kurze Aussage darüber, dass die Eingaben geprüft werden müssen.
Slot error-messageInhalt der Fehlerzusammenfassung. Hier stehen die einzelnen Fehlermeldungen des Formulars.
Slot topInhalt oberhalb des Formularinhalts, zum Beispiel eine Einleitung. Er liegt im selben Container part="top" wie das eingebaute Fehler-Callout und wird im Markup der Komponente nach diesem ausgegeben. Da part="top" ein normales Blockelement ist, stehen beide untereinander: zuerst die Fehleranzeige, darunter der Inhalt aus dem Slot top.
Standard-SlotDer eigentliche Formularinhalt, also die Form Layouts und ein optionales Block Layout.
Partstop, default-error und form-content für die Gestaltung. Das eingebaute Fehler-Callout gibt zusätzlich title, default-title, text und state nach außen weiter. Die exportparts-Angabe von <mate-form> führt daneben den Namen content auf, den <mate-callout> gar nicht rendert; dieser Name greift deshalb ins Leere.

Alles Weitere leistet die Anwendung: Sie löst die Validierung aus, entscheidet wann invalid gesetzt und wieder entfernt wird, formuliert und füllt die Fehlerliste, verweist darin auf das betroffene Feld und setzt nach dem Absenden den Fokus auf die Fehlerzusammenfassung. Die Form selbst validiert nicht und sammelt keine Feldfehler ein.

Info für Dev

Die Attribute has-error-title und has-error-message werden von der Komponente automatisch gesetzt und wieder entfernt, sobald ein Fehlertitel gesetzt beziehungsweise Inhalt in den Slot error-message eingehängt wurde. Es sind keine Steuerattribute und sie werden nicht manuell gesetzt.
Für die Darstellung wertet die Theme-CSS nur has-error-title aus: Die Theme-Datei base/callout.css rückt über den Selektor mate-form:not([has-error-title])::part(text) den Text in die erste Rasterzeile, wenn kein Fehlertitel gesetzt ist. Für has-error-message gibt es im gesamten Theme keine Regel. Es ist ein reines Zustandsattribut, das die Anwendung abfragen kann.

Die Fehlerzusammenfassung ersetzt nicht die Fehlermeldung am einzelnen Feld. Beides gehört zusammen: Die Form nennt alle Fehler gesammelt am Anfang, das jeweilige Eingabeelement zeigt seinen eigenen State Error direkt am Feld.

Formularspalten

Nach der Mate-Regel stehen für Eingaben maximal zwei Formularspalten zur Verfügung. Eine zusätzliche Kontextspalte mit einem Callout ist erlaubt und zählt nicht als dritte Formularspalte, solange dort keine Eingabeelemente des Formulars stehen.

text
Mate-konform:
[ Formularspalte 1 ] [ Formularspalte 2 ] [ Info-Callout ]

Nicht Mate-konform:
[ Formularspalte 1 ] [ Formularspalte 2 ] [ Formularspalte 3 ]

Eine dritte Spalte mit Eingabefeldern ist nach dieser Regel nicht vorgesehen. Sie verlängert die Lesewege, erschwert die Tastaturbedienung und bricht mit dem einheitlichen Erscheinungsbild aller Formulare. Reicht der Platz nicht aus, werden die Feldergruppen untereinander angeordnet, nicht enger nebeneinander. Auch diese Vorgabe wird technisch nicht erzwungen: Ein Block Layout nimmt bis zu drei Bereiche nebeneinander auf, unabhängig davon, was darin steht.

Die Regeln innerhalb einer Formularspalte sind beim Form Layout beschrieben: maximal zwei Eingabeelemente nebeneinander, ein einzelnes Eingabeelement nutzt die volle Breite der Formularspalte und eine Textarea nimmt immer die volle Breite ein.

Responsives Verhalten

Die Form selbst ist nicht responsiv. Sie ist ein Flex-Container in Spaltenrichtung und stellt ihre Kinder in jeder Breite untereinander, jedes Kind über die volle Breite. Ein einspaltiges Formular bleibt deshalb auf allen Geräten einspaltig.

Eine mehrspaltige Anordnung entsteht ausschließlich über ein Block Layout innerhalb der Form. Maßgeblich ist dann dessen Containerbreite, nicht die Breite des Browserfensters: Andere Bereiche der Anwendung, etwa ein aufgeklapptes Menü oder ein Side Panel, verändern die verfügbare Breite und damit die Anordnung.

Für das Beispiel oben mit zwei Feldergruppen und einem Callout in der Standardkonfiguration des Block Layouts ergibt sich:

ContainerbreiteAnordnung
bis 768 PixelBeide Feldergruppen und der Callout stehen untereinander.
über 768 bis 1120 PixelDie beiden Feldergruppen stehen nebeneinander, der Callout darunter über die volle Breite.
über 1120 PixelDie beiden Feldergruppen und der Callout stehen nebeneinander.

Andere Konfigurationen des Block Layouts führen zu anderen Aufteilungen. Sie sind beim Block Layout beschrieben.

Offener Punkt für Dev

Die Theme-Datei base/form.css enthält Regeln unter @container form (...), darunter mate-form > :nth-of-type(2n):not([slot="top"]) sowie Spaltenvorgaben für mate-form::part(top) und mate-form::part(form-content).
Diese Regeln sind im Theme vorbereitet, in Mate 25 aber ohne Wirkung: Im Repository setzt keine Regel einen Container mit dem Namen form, es gibt nur die Klasse block-container (Containername block) und das Element mate-filter-panel (Containername filter). Zusätzlich erhalten die beiden Parts zwar grid-template-columns, aber kein display: grid. Verlassen Sie sich nicht auf dieses Verhalten. Die Klärung mit dem Komponententeam ist offen.

Barrierefreiheit

<mate-form> liefert selbst keine Formularsemantik. Die Komponente rendert ausschließlich div-Elemente: kein natives <form>-Element, keine Landmark-Rolle und keine Live-Region am Formular selbst. Formularsemantik und Absendeverhalten verantwortet deshalb die Anwendung, ebenso ein umschließendes <form>, falls das Formular abgeschickt werden soll.

Auch die Ansage der Fehlerzusammenfassung liegt bei der Anwendung. Das eingebaute Fehler-Callout liegt dauerhaft im Shadow DOM und wird nur über display: none verborgen, solange invalid nicht gesetzt ist; ob eine Vorlesehilfe das Einblenden ansagt, ist damit nicht verlässlich. Nach dem Absenden ist der Fokus auf die Fehlerzusammenfassung zu setzen oder die Meldung zusätzlich über eine eigene Live-Region auszugeben.

Jedes Eingabeelement innerhalb der Form braucht ein sichtbares, dauerhaft lesbares Label. Platzhaltertexte ersetzen kein Label. Pflichtfelder werden einheitlich gekennzeichnet, und die Bedeutung der Kennzeichnung wird einmal am Anfang des Formulars erklärt.

Die Fehlerzusammenfassung am Anfang der Form ist der zentrale Einstiegspunkt nach einer fehlgeschlagenen Validierung. Sie nennt jeden Fehler in verständlicher Sprache und verweist auf das betroffene Feld, sodass Benutzerinnen und Benutzer direkt dorthin springen können. Nach dem Absenden eines fehlerhaften Formulars setzt die Anwendung den Fokus auf die Fehlerzusammenfassung, damit Screenreader sie ausgeben und die Tastaturbedienung an der richtigen Stelle fortgesetzt wird.

Das eingebaute Fehler-Callout ist erst sichtbar, wenn das Formular als nicht valide markiert ist. Es ergänzt die Fehlermeldung am einzelnen Feld und ersetzt sie nicht. Fehlertexte beschreiben, was zu tun ist, und nicht nur, dass etwas falsch ist.

Die Fokusreihenfolge folgt der Reihenfolge im DOM, also der Reihenfolge der Feldergruppen: Zuerst werden alle Felder einer Feldergruppe durchlaufen, danach folgt die nächste. Innerhalb einer Feldergruppe läuft der Fokus zeilenweise von links nach rechts. Stehen zwei Formularspalten nebeneinander, kann der Fokus deshalb von der rechten Spalte zurück in die linke springen, sobald dort die nächste Feldergruppe beginnt. Die Feldergruppen sind im Markup so anzuordnen, dass diese Reihenfolge fachlich nachvollziehbar bleibt. Details dazu stehen beim Form Layout.

Develop Web Components

Installation, Import und die Grundvarianten stehen in den Beispielen am Ende dieser Seite. Ergänzend gelten die folgenden Punkte.

Die Feldergruppen und das Block Layout kommen über @mate/theme und benötigen keinen eigenen Import. Ein Import ist nur für <mate-form> selbst, für <mate-callout> und für die verwendeten Vaadin-Felder nötig.

Der State Error wird über das Attribut invalid und den Titel error-title gesetzt. Die einzelnen Meldungen werden in den Slot error-message eingehängt, nicht über eine Eigenschaft übergeben.

html
<mate-form invalid error-title="Bitte prüfen Sie Ihre Eingaben">
  <ul slot="error-message">
    <li>Die Personalnummer ist bereits vergeben.</li>
    <li>Das Einstellungsdatum liegt in der Zukunft.</li>
  </ul>
  <div theme="form-layout spacing">
    <vaadin-text-field label="Personalnummer" invalid error-message="Bereits vergeben"></vaadin-text-field>
  </div>
</mate-form>

Voraussetzungen: @mate/form, @mate/theme und @vaadin/text-field.

Develop Vue

shell
npm i @mate-vue/form
javascript
import { MateForm } from '@mate-vue/form';

In Vue liegt jede Komponente in einem eigenen Paket, auch die Eingabefelder. Für die Beispiele unten wird neben der Form das Form Layout benötigt, dazu je Feldtyp das passende Feldpaket:

shell
npm i @mate-vue/form-layout @mate-vue/text-field @mate-vue/email-field @mate-vue/password-field
javascript
import { MateFormLayout } from '@mate-vue/form-layout';
import { MateTextField } from '@mate-vue/text-field';
import { MateEmailField } from '@mate-vue/email-field';
import { MatePasswordField } from '@mate-vue/password-field';

Ein Formular mit einer einzigen Feldergruppe enthält sein Form Layout direkt in der Form:

vue
<template>
  <MateForm>
    <MateFormLayout>
      <h5>Zugangsdaten</h5>
      <MateTextField label="Benutzername"></MateTextField>
      <MateEmailField label="E-Mail-Adresse"></MateEmailField>
      <MatePasswordField label="Passwort"></MatePasswordField>
    </MateFormLayout>
  </MateForm>
</template>

Sobald mehrere Feldergruppen in einer Form stehen, gilt auch in Vue, was der Abschnitt „Aufbau“ beschreibt: Die Form setzt zwischen ihren Bereichen keinen Abstand. Dafür wird ein Layout innerhalb der Form benötigt, das seine Kinder vertikal stapelt und Spacing aktiviert hat; sollen die Bereiche nebeneinander stehen, übernimmt das Block Layout diese Aufgabe. Ein Vertical Layout ist für die Vue-Pakete nicht dokumentiert. Klären Sie im Projekt, welche Komponente diese Aufgabe übernimmt, bevor Sie mehrere Feldergruppen in eine Form setzen.

Die Fehlerzusammenfassung wird in Vue über die Eigenschaften invalid, error-title und error-items gesteuert.

vue
<template>
  <MateForm
    invalid
    error-title="Bitte prüfen Sie Ihre Eingaben"
    :error-items="['Die Personalnummer ist bereits vergeben.', 'Das Einstellungsdatum liegt in der Zukunft.']">
    <MateFormLayout>
      <MateTextField label="Personalnummer" invalid errorMessage="Bereits vergeben"></MateTextField>
    </MateFormLayout>
  </MateForm>
</template>

Das Form Layout heißt in Vue MateFormLayout und stammt aus dem oben genannten Paket @mate-vue/form-layout.

Develop Flow

Für die Form und die zugehörigen Layouts werden zwei Artefakte benötigt.

xml
<dependency>
    <groupId>de.mate_ds</groupId>
    <artifactId>mate-form-flow</artifactId>
</dependency>
<dependency>
    <groupId>de.mate_ds</groupId>
    <artifactId>mate-flex-layout-flow</artifactId>
</dependency>
java
import de.mate_ds.flow.component.form.Form;
import de.mate_ds.flow.component.flexlayout.FormLayout;
import de.mate_ds.flow.component.flexlayout.BlockLayout;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H5;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import java.util.stream.Stream;

Auch in Flow setzt die Form keinen Abstand zwischen ihren Bereichen. Mehrere Form Layouts direkt über form.add(...) stehen deshalb ohne Abstand aneinander. Ein einspaltiges Formular erhält daher ein VerticalLayout zwischen der Form und den Feldergruppen:

java
Form form = new Form();

FormLayout person = new FormLayout();
person.setSpacing(true);
person.add(new H5("Person"), new TextField("Vorname"), new TextField("Nachname"));

FormLayout adresse = new FormLayout();
adresse.setSpacing(true);
adresse.add(new H5("Adresse"), new TextField("Straße"), new TextField("Hausnummer"));

VerticalLayout spalte = new VerticalLayout(person, adresse);
spalte.setSpacing(true);
spalte.setPadding(false);

form.add(spalte);

Das VerticalLayout stammt aus com.vaadin.flow.component.orderedlayout und ist eine Vaadin-Komponente, keine Mate-Klasse. Die Vaadin-Dokumentation zu Vertical Layout führt spacing als Stilvariante auf und beschreibt Spacing als standardmäßig aktiviert, abschaltbar über setSpacing(false); setSpacing(true) hält es entsprechend aktiv. Genau diesen Theme-Namen wertet die Theme-Datei size/base/flex-layout.css aus, die vaadin-vertical-layout mit erfasst und dafür gap: var(--mate-spacing-margin) setzt. Padding ist beim VerticalLayout ebenfalls standardmäßig aktiviert; innerhalb der Form ist es unerwünscht und wird über setPadding(false) abgeschaltet.

Sollen die Feldergruppen nebeneinander stehen, tritt an die Stelle des VerticalLayout ein Block Layout:

java
Form form = new Form();

FormLayout person = new FormLayout();
person.setSpacing(true);
person.add(new H5("Person"), new TextField("Vorname"), new TextField("Nachname"));

FormLayout adresse = new FormLayout();
adresse.setSpacing(true);
adresse.add(new H5("Adresse"), new TextField("Straße"), new TextField("Hausnummer"));

BlockLayout blockLayout = new BlockLayout();
blockLayout.setSpacing(true);
blockLayout.add(person, adresse);

Div container = BlockLayout.createContainer(blockLayout);
form.add(container);

BlockLayout.createContainer erzeugt den erforderlichen umgebenden Container. Ohne ihn bleiben die Bereiche untereinander.

Die Fehlerzusammenfassung am Anfang des Formulars wird über die Form gesetzt.

java
form.setInvalid(true);
form.setErrorTitle("Bitte prüfen Sie Ihre Eingaben");
form.setErrorItems(Stream.of("Die Personalnummer ist bereits vergeben.", "Das Einstellungsdatum liegt in der Zukunft."));

FormLayout ist nicht gleich FormLayout

In Flow wird de.mate_ds.flow.component.flexlayout.FormLayout verwendet, nicht com.vaadin.flow.component.formlayout.FormLayout. Beide Klassen heißen gleich, verhalten sich aber unterschiedlich. Der Import ist zu prüfen.