# 📋 Kapitel 5: Ein Formular zum Adoptieren hinzufügen

Ziel Implementiere ein Formular, um einen Hund zu adoptieren
Was du lernen wirst Formulare in einer Vue-App erstellen und validieren
Was du dafür benötigst Einen modernen Browser, z.B. Google Chrome. Ein Account bei CodeSandbox.io. Falls du nicht mehr weißt, wo du warst, kannst du die Basis für dieses Kapitel von hier (opens new window) importieren. Wie das geht, steht im Anhang 1
Dauer 1 Stunde

# Anleitung

Falls du das Projekt von vorn beginnen musst, klone dieses Projekt (opens new window) in Code Sandbox, nachdem du dich eingeloggt hast. Dafür klickst du auf den Link Import form Github unten links auf der Hauptseite und fügst die URL des Repositories in das Feld. Du kannst ebenfalls mit dem Projekt fortfahren, dass du in Kapitel 4 erstellt hast.

In diesem Kapitel implementieren wir ein Formular, das man ausfüllen kann nachdem man Hunde auf die Favoriten-Liste gesetzt hat. Zuerst müssen wir eine neue Komponente erstellen, die dieses Formular beinhaltet, und eine neue Route für dieses Formular im Router konfigurieren.

# Gerüst für die Formular-Komponente

Erstelle eine neue Datei Form.vue im views-Ordner.

Schreibe den <template></template> in die neue Datei. Füge ein div-Tag hinzu und schreibe dort hinein den Text This form works!. So sollte die Komponente jetzt aussehen:

Copy
<template>
	<div>
		This form works!
	</div>
</template>
1
2
3
4
5

Jetzt kommt die Route für diese neue Komponente. Importiere die Form-Komponente in main.js:

Copy
import Form from './views/Form';
1

Füge eine neue Option in der routes-Liste hinzu:

Copy
{ path: "/form", component: Form }
1

Überprüfen wir mal, wie das funktioniert. Rufe die /form-Route auf, indem du /form and die Shop-URL anhängst. Du solltest den Text 'This form works!' zwischen dem Header und Footer sehen.

Fügen wir eine Klasse an das div, um es etwas zu gestalten.

Copy
<div class="form-wrapper">
	This form works!
</div>
1
2
3

Füge einen <style scoped></style> Block unter den <template> Block ein. In diesem Tag werden wir ein paar Styles für unsere Formular-Komponente definieren. Zuerst ein Padding (=innerer Rand) für form-wrapper:

Copy
<style scoped>
	.form-wrapper {
		padding: 40px;
	}
</style>
1
2
3
4
5

# Das Formular bauen

Jetzt ist es an der Zeit das eigentliche Formular zu bauen. Wir werden dafür die Vuetify-Komponente v-form nutzen.

💡

Mehr über Vuetify-styled Formulare kannst du in der Dokumentation (opens new window) nachlesen.

Als erstes fügen wir eine leere v-form in den form-wrapper ein:

Copy
<template>
	<div class="form-wrapper">
		<v-form> </v-form>
	</div>
</template>
1
2
3
4
5

So wird natürlich noch nichts angezeigt. Dazu müssen wir Formularfelder hinzufügen.

Für die Formularfelder nutzen wir die Vuetify-Komponente namens v-text-field. Diese hat ein Attribut label, mit dem wir ein Label (=Beschreibung/Namen) für das Feld definieren können. Wir erstellen Felder mit den Namen "Name", "Email" und "Phone" in v-form:

Copy
<div class="form-wrapper">
	<v-form>
		<v-text-field label="Name"></v-text-field>
		<v-text-field label="Email"></v-text-field>
		<v-text-field label="Phone"></v-text-field>
	</v-form>
</div>
1
2
3
4
5
6
7

Sieht schon besser aus!

# Einen Absenden-Button hinzufügen

Irgendwie muss das Formular abgeschickt werden. Dafür fügen wir einen Absenden (=submit) Button unter die Formularfelder ein:

Copy
<div class="form-wrapper">
	<v-form>
		<v-text-field label="Name"></v-text-field>
		<v-text-field label="Email"></v-text-field>
		<v-text-field label="Phone"></v-text-field>
		<v-btn>Submit</v-btn>
	</v-form>
</div>
1
2
3
4
5
6
7
8

Unser Button ist erstmal links ausgerichtet. Um ihn zu zentrieren, schreiben wir text-align: center in die form-wrapper Styles:

Copy
.form-wrapper {
	padding: 40px;
	text-align: center;
}
1
2
3
4

Der Submit-Button macht erstmal noch nichts. Wir werden eine Methode hinzufügen, die alle Werte der Formularfelder in der Konsole ausgibt. Dafür müssen wir eine Property für jedes Feld in den Komponenten-data schreiben und diese mit den Feldern über die v-model-Direktive verknüpfen.

💡

Die v-model-Direktive erzeugt eine bi-direktionale Verknüpfung zwischen Formular- und Textfeld-Elemente. Sie wählt automatisch den richtigen Weg basierend auf dem Feldtyp, um den Wert zu aktualisieren.

# Daten-Verknüpfung

💡

Was bedeutet bi-direktionale Verknüpfung? Das bedeutet, dass wir das data-Attribut entweder direkt über die verknüpfte Komponente oder innerhalb der Komponente selbst verändern können und der neue Wert wird automatisch an beiden Stellen aktualisiert.

Fügen wir einen <script></script>-Block über die Styles ein, füge das export dfault ein und implementiere die data-Komponente (data sollte eine Funktion sein, die ein Objekt liefert):

Copy
<script>
	export default {
	  data() {
	    return {

	    }
	  }
	}
</script>
1
2
3
4
5
6
7
8
9

Jetzt fügen wir die neuen Properties dem Objekt hinzu:

Copy
data() {
	return {
	    name: "",
	    email: "",
	    phone: ""
	};
	}
1
2
3
4
5
6
7

Wie du siehst, sind alles zunächst leere Texte (=string).

Verknüpfe diese Properties mit den entsprechenden Formularfeldern im Template indem du die v-model-Direktive hinzufügst:

Copy
<v-form>
	<v-text-field label="Name" v-model="name"></v-text-field>
	<v-text-field label="Email" v-model="email"></v-text-field>
	<v-text-field label="Phone" v-model="phone"></v-text-field>
	<v-btn>Submit</v-btn>
</v-form>
1
2
3
4
5
6

Ändere jetzt den Wert der name-Property in data anstelle des leeren Textes (z.B. zu deinem eigenen Namen). Das Formularfeld hat sich verändert! Wenn du etwas in das Textfeld schreibst, wird die verknüpfte data-Property ebenfalls aktualisiert. So funktioniert bi-direktionale Verknüpfung.

Jetzt können wir die Eingaben aus dem Formular in der Konsole ausgeben, wenn das Formular abgeschickt wird. Dafür implementieren wir eine Methode (erstelle die methods direkt nach der data-Funktion, vergiss nicht das Komma nach der schließenden Klammer von data):

Copy
methods: {
  submit() {
    console.log(
      "Name:",
      this.name,
      "Email:",
      this.email,
      "Phone:",
      this.phone
    );
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

and verknüpfe es mit dem Klick auf den Submit-Button:

Copy
<v-btn @click="submit">Submit</v-btn>
1

Fülle das Formula mit Testdaten aus und klicke auf Submit. Du kannst sehen, dass die Daten aus dem Formular in der Code Sandbox Konsole ausgegeben werden.

# Die abgeschickten Informationen anzeigen

Ausgaben in der Konsole sind schon ganz gut, aber so sollte das nicht in der fertigen App funktionieren. Anstatt die Werte in der Konsole auszugeben, zeigen wir sie anstelle des Formulars an. Zuerst brauchen wir natürlich einen Indikator, der überprüft, ob das Formular bereits abgeschickt wurde.

Dafür erstellen wir eine neue Property in data names submitted und setzen diese initial auf false (wenn die Komponente erstellt wird, sollte das Formular noch nicht abgeschickt sein):

Copy
data() {
  return {
    name: "",
    email: "",
    phone: "",
    submitted: false
  };
},
1
2
3
4
5
6
7
8

Jetzt müssen wir submitted auf true setzen, wenn das Formular abgeschickt wird. Diese Logik fügen wir in die submit-Methode ein anstelle der console.log-Befehle:

Copy
methods: {
  submit() {
    this.submitted = true;
  }
}
1
2
3
4
5

Jetzt müssen wir noch ein div erstellen, das das Formular ersetzt. Kopiere diesen Code über das <v-form>-Tag:

Copy
<div class="text-xs-center">
	<h2>Thank you for you interest, we will contact you soon</h2>
	<div class="details text-xs-left">
		<h3 class="blue-grey--text">Customer details</h3>
		<p><strong>Name:</strong> {{name}}</p>
		<p><strong>Email:</strong> {{email}}</p>
		<p><strong>Phone:</strong> {{phone}}</p>
	</div>
	<v-btn to="/">Go to homepage</v-btn>
</div>
1
2
3
4
5
6
7
8
9
10

und füge dafür ein paar Styles ein in den <style>-Tag:

Copy
.details {
	padding-top: 30px;
}
h3 {
	padding-bottom: 20px;
}
1
2
3
4
5
6

# Daten von einer Bedingung abhängig anzeigen

Jetzt sehen wir sowohl das div mit unseren Informationen aus dem Formular als auch das Formular selbst. Das sieht komisch aus.

Wir wollen sie abhängig von einer Bedingung anzeigen lassen. Wir werden das div anzeigen, wenn submitted true ist; ansonsten wird das Formular angezeigt.

Also fügen wor ein v-if="submitted" dem div und ein v-else dem Formular v-form hinzu:

Copy
<div class="text-xs-center" v-if="submitted">
	...
</div>
<v-form v-else>
	...
</v-form>
1
2
3
4
5
6

Jetzt wird das Formular nach dem Abschicken versteckt und die zuvor eingegebenen Informationen werden angezeigt.

# Validierung hinzufügen

Jetzt fehlt noch eine richtige Validierung für das Formular. Zunächst bauen wir aber erstmal einen Button ein, der zum Formular führt. Öffne die Favorites.vue und kopiere folgenden Code nach dem schließenden </v-list-item>-Tag.

Copy
<v-btn to="/form">Adopt</v-btn>
1

Super! Jetzt können wir ganz einfach zum Formular navigieren. Die Validierung fehlt trotzdem noch. Im Moment können wir einfach irgendetwas in das E-Mail-Feld eintragen oder Buchstaben als Telefonnummer abschicken. Wir können sogar ein leeres Formular abschicken!

Um das zu ändern, müssen wir erstmal eine neue data-Property namens valid hinzufügen und diese mit der v-model-Direktive mit der v-formverknüpfen. Bearbeite in Form.vue das data-Objekt:

Copy
data() {
	return {
	    name: "",
	    email: "",
	    phone: "",
	    submitted: false,
	    valid: true
	};
},
1
2
3
4
5
6
7
8
9

Verknüpfe das Formular mit der neuen valid-Property:

Copy
<v-form v-else v-model="valid"></v-form>
1

Wir deaktivieren den Abschicken-Button, solange die eingegebenen Informationen nicht valide sind:

Copy
<v-btn @click="submit" :disabled="!valid">Submit</v-btn>
1

Jetzt können wir Validierungsregeln schreiben.

💡

Alle Eingabefelder in der v-form haben eine rules-Property, die eine Liste von Funktionen entgegen nimmt. Sobald sich der Eingabewert eines Feldes ändert, erhalten alle Funktionen in dieser Liste den neuen Wert. Die Validierung schlägt fehlt, sobald eine dieser Funktionen false oder einen String zurück liefert. Vuetify wird diese Ergebnisse dafür benutzen, um das v-model auf true oder false zu setzen. Indem wir also das Attrribut v-model="valid" in unserem Tag v-form, werden wir wissen, ob data in unserem Formular valide ist oder nicht. Wir werden diese Information benutzen, um den Submit Button zu deaktivieren, falls notwendig. Aktuell wäre unser Button niemals deaktiviert, da wir noch keine Regeln geschrieben haben, um das v-model zu validieren - daher ist die Form immer valide.

# Validierung 1: Name

Zuerst wollen wir ein leeres name-Feld verbieten. Dazu schreiben wir eine nameRules-Eigenschaft in unsere data:

Copy
data() {
  return {
    name: "",
    email: "",
    phone: "",
    submitted: false,
    valid: true,
    nameRules: []
  };
},
1
2
3
4
5
6
7
8
9
10

Jetzt kommt die erste Regel. Denk dran, Validierungsregeln sind Funktionen, die den Wert des Feldes erhalten und einen Bool'schen Wert zurück geben; true bedeutet, dass das Feld einen korrekten/validen Wert beinhaltet, false bedeutet, dass der Wert nicht korrekt ist. Also sieht die erste Regel so aus:

Copy
nameRules: [(name) => !!name];
1

Was passiert hier? !name gibt true zurück, wenn der Name leer ist und andernfalls false. Mit der zweiten Verneinung kehren wir das noch einmal um. Die doppelte Verneinung wird oft genutzt, um zu überprüfen, ob ein String nicht-leer ist.

Füge nameRules zu der rules-Property des name-Feldes hinzu und markiere das Feld zusätzlich als required (=Pflichfeld):

Copy
<v-text-field label="Name" required :rules="nameRules" v-model="name"></v-text-field>
1

Klicke jetzt in das Name-Feld und dann in ein anderes. Du sieht, wie Name rot wird und darunter der Text false erscheint. (Der Submit-Button ist immer noch deaktiviert.)

Fehlermeldungen können mit Hilfe des ||-Operators in der Regel ergänzt werden. Der Wert der Validierung ist also false OR <Fehlermeldung>. Lass uns die Fehlermeldung für das Name-Feld verbessern:

Copy
nameRules: [(name) => !!name || 'Name is required'];
1

Jetzt sieht die Fehlermeldung doch schon besser aus!

Wir fügen eine weitere Regel hinzu: Ein Name darf nicht kürzer als zwei Buchstaben sein:

Copy
nameRules: [
	(name) => !!name || 'Name is required',
	(name) => name.length > 2 || 'Name must be longer than 2 characters',
];
1
2
3
4

Schreibe nur einen Buchstaben in das Name-Feld, die neue Fehlermeldung sollte angezeigt werden.

# Validierung 2: Email

Jetzt wechseln wir zum E-Mail-Feld. Zuerst erstellen wir die emailRules-Property in data und fügen den nicht-leeren Check wie zuvor auch beim Namen hinzu:

Copy
emailRules: [(email) => !!email || 'Email is required'];
1

Vergiss nicht required und die rules-Property an das E-Mail-Feld zu schreiben:

Copy
<v-text-field label="Email" required :rules="emailRules" v-model="email"></v-text-field>
1

Die zweite Regel für das E-Mail-Feld ist etwas komplizierter. Wir wollen überprüfen, ob die eingegebene E-Mail einem bestimmten Muster entspricht, diese Muster heißen Reguläre Ausdrücke (=regular expressions / RegEx).

💡

Reguläre Ausdrücke sind Muster, die Strings auf bestimmte Buchstaben/Zahlen-Kombinationen überprüfen. In JavaScript sind Reguläre Ausdrücke auch Objekte.

Mehr Informationen zu RegEx kannst du in diesem MDN Artikel (opens new window) nachlesen.

Füge den Regulären Ausdruck zu den Validierungsregeln hinzu:

Copy
emailRules: [
	(email) => !!email || 'Email is required',
	(email) => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(email) || 'Email must be valid',
];
1
2
3
4

Gebe jetzt irgendwelche zufälligen Zeichen im E-Mail-Feld ein. Du siehst die Fehlermeldung; eine E-Mail muss ein @-Zeichen, einen Punkt und mindestens zwei Zeichen nach dem Punkt haben. Wenn du versuchst, deine eigene E-Mail-Adrresse einzufügen, wirst du sehen, dass ein Fehler angezeigt wird.

# Validierung 3: Telefon

Jetzt wechseln wir zum phone-Feld. Wir erstellen ein ähnliches Regelset wie für name. Die Telefonnummer sollte mindestens sieben Zeichen lang sein:

Copy
phoneRules: [
	(phone) => !!phone || 'Phone is required',
	(phone) => phone.length >= 7 || 'Phone number should be at least 7 digits',
];
1
2
3
4

Aber man kann immer noch Buchstaben eingeben. Und die Telefonnummer wird nicht formatiert. Um das zu erreichen, werden wir die vue-the-mask hinzufügen. Dafür klicke in Code Sandbox auf den Reiter Explorer -> Dependencies -> Add Dependency und suche nach vue-the-mask. Wenn du es installiert hast, wirst du sehen, dass es zu deiner package.json hinzugefügt wurde. Nun wurde vue-the-mask installiert, wir müssen es nun noch der Komponente als Direkte hinzufügen.

Als erstes, fügst du vue-the-mask in deiner Form.vue hinzu:

Copy
import { mask } from 'vue-the-mask';
1

Im Anschluss, fügst du die Direktive direkt vor data in deinem <script> Block hinzu:

Copy
directives: {
  mask,
},
1
2
3

💡

Indem das Objekt directives unserer Komponent mit einem Element mask hinzugefügt wurde, registieren wir mask in dieser Komponente. Das bedeutet, dass wir von nun an, in dieser Komponent, v-mask bei jedem beliebigen Element verwenden können. Alle Direktiven in Vue besetzen das Kürrzel v- am Anfang, Daher wird aus der Direktive mask dann automatisch v-mask. Mehr Information findest du hier (opens new window).

Nachdem wir nun v-mask als Direktive zur Verfügung stehen haben, können wirr folgendes in unser Tag v-text-field hinzufügen:

Copy
v-mask="'(###) ### - ####'"
1

Indem wir '(###) ### - ####' als Input für die Maske benutzen, limitieren wir die Möglichkeiten der Eingabe zu Ziffern in diesem spezifischen Format. Das bedeutet, dass es möglich ist Nummern wir (555) 555-1234 einzugeben, nicht aber Ziffern in einem anderen Format oder Charaktere, die keine Ziffern sind. Sollte dein Land ein anderes Format benutzen, ändere es nun gerne ab.

Im Ganzen sollte das Tag nun folgendermaßen aussehen:

Copy
<v-text-field label="Phone" required :rules="phoneRules" v-mask="'(###) ### - ####'" v-model="phone"></v-text-field>
1

Jetzt kannst du nur Zahlen eingeben und die Telefonnummer wird direkt formatiert.

# Lösche alle Favoriten beim Absenden des Formulars

Als letztes möchten wir noch die Favoritenliste wieder leeren, wenn wir das Formular absenden. Gehe dazu in store/store.js und kopiere den folgenden Code in das mutations-Objekt:

Copy
clearFavorites(state) {
  state.favorites = [];
}
1
2
3

Füge die Aktion zum Aufrufen der neuen Mutation zu actions hinzu:

Copy
clearFavorites({ commit }) {
  commit("clearFavorites");
}
1
2
3

Gehe zurück in die Form.vue-Datei und rufe die neue Aktion in der submit-Methode auf:

Copy
submit() {
  this.$store.dispatch("clearFavorites");
  this.submitted = true;
}
1
2
3
4

Jetzt wird die Favoritenliste geleert, nachdem das Formular abgesendet wird.

🎊Herzlichen Glückwunsch, du hast das Projekt abgeschlossen!🎊

# Ergebnis

chapter 5 final