📋 Kapitel 2: Baue eine Haustier-Galerie

Ziel Lerne, wie du Daten in der Webanwendung verändern kannst
Was du lernen wirst Verwendung von statischen Daten. Du baust eine Oberfläche aus Kacheln, die Adoptivhunde anzeigt
Was du dafür benötigst Einen modernen Browser, z.B. Google Chrome. Einen Account bei CodeSandbox.io. Falls du nicht mehr weißt, wo du warst, kannst du die Basis für dieses Kapitel von hier importieren. Wie das geht, steht im Anhang 1
Dauer 1 Stunde

Anleitung

Falls du das Projekt von vorn beginnen musst, klone dieses Projekt 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 1 erstellt hast.

Aktuell hat unser Pet Shop nur eine Homepage. Wir möchten eine weitere Seite hinzufügen, auf der in mehreren Kacheln Haustiere angezeigt werden können. Wir erstellen eine Single-Page Anwendung mit einer Navigation und zwei Navigationspunkten: "home" und "pets" (=Haustiere). Wenn man auf "pets" klickt, wird die neue Seite angezeigt, die wir jetzt erstellen und "home" wird eine Seite öffnen, die wir in Kapitel 1 erstellt haben.

💡

"A single-page application (SPA) is a web application or web site that interacts with the user by dynamically rewriting the current page rather than loading entire new pages from a server" (Wikipedia)

Um eine SPA mit Vue zu bauen, benötigen wir den vue-router. Der vue-router ist der offizielle Router für Vue.js, die Bibliothek mit der man einfach und effizient zwischen meheren Seiten navigieren kann. Der vue-router ist genau für die Verwendung mit SPAs gebaut. SPAs haben einige spezifische Anforderungen, wie z.B. verschachtelte Routen oder Datenübertragung. Füge den vue-router zu den Abhängigkeiten deiner Vue-App hinzu (Klicke auf Add Dependency und suche nach vue-router). Du wirst sehen, dass es in dem depencendies Objekt in unserer package.json hinzugefügt wurde.

Der Router

Öffne die Datei main.js und importiere vue-router:

Copy
import VueRouter from 'vue-router';
1

Du solltest nun diese vier Import-Zeilen hinzufügen:

Copy
import Vue from 'vue';
import App from './App.vue';
import vuetify from '@/plugins/vuetify';
import VueRouter from 'vue-router';
1
2
3
4

Jetzt müssen wir das Plugin bei der Vue-App registrieren mit Hilfe der globalen Vue.use()-Methode:

Copy
Vue.use(VueRouter);
1

Füge diese Zeile vor new Vue(...) hinzu, um sicherzustellen, dass jede neue Instanz von Vue, die wie erstellen, den vue-router benutzen wird. Wenn du die Zeile hinter new Vue(...) hinzufügen würdest, würde der vue-router in unserer Applikation nicht zur Verfügung stehen.

💡

Überlege für einen Moment, wie die App aufgebaut sein muss. Der Header und Footer sollen auf jeder Seite gleich sein. Der eigentliche Inhalt dazwischen soll sich verändern, je nach dem auf welchem Navigationspunkt man klickt. Die Komponente, die zu der Route (=dem geklickten Navigationspunkt) passt, wird in einem <router-view>-Tag angezeigt. Das heißt wir müssen unseren Code verändern, damit nicht mehr alles in der App.vue steht. Weil der Header und Footer jederzeit sichtbar sein sollen, bleiben sie in der App.vue. Der Inhalt der spezifischen Seiten wird in separate Komponten veschoben. Wir werden somit davon wegkommen, dass alle Teile unserer Applikation in die App.vue sind, wir werden also ein Refactoring durchführen.

Eine Homepage erstellen

Wir erstellen eine separate Komponente für alle Elemente in <div class="wrapper">.

  • Gehe in den views-Ordner in src, falls dieser nicht existiert, erstelle ihn zuerst. In diesem Ordner erstelle eine Datei namens Home.vue.

  • Füge den <template></template>-Tag in diese Datei ein.

  • Öffne die App.vue-Datei. Kopiere das <div class="wrapper"> und alle Elemente darin in dem template-Tag in Home.vue. Du solltest dort nun allen Code, der zwischen <header> und <footer> stand, stehen haben. Lösche diesen Teil aus App.vue.

Du wirst sehen, dass unsere Applikation etwas leer aussieht, aber keine Sorge - wir werden die entfernten Bestandteile später wieder hinzufügen.

Eine Haustier-Seite erstellen

Jetzt erstellen wir eine Seite für die Haustiere. Erstelle im src/views-Ordner eine neue Datei Pets.vue, genauso wie du es mit Home.vue gemacht hast. Kopiere folgenden Code für das Layout der Seite.

Copy
<template>
	<v-container grid-list-md fluid>
		<v-layout wrap>
			<v-flex xs12 sm4 md3>
				<v-card color="grey lighten-2">
					<v-img src="https://goo.gl/6CQNDo" height="170px"> </v-img>
					<v-card-title>
						<div>
							<h3>Looking for a dog?</h3>
						</div>
					</v-card-title>
				</v-card>
			</v-flex>
		</v-layout>
	</v-container>
</template>

<style scoped>
	p {
		margin: 0;
	}
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

Die Routen hinzufügen

Super! Jetzt haben wir zwei verschiedene Komponenten für unsere Startseite und die Haustier-Galerie. Wie du sicher schon bemerkt hast, wird das aber noch nicht in der App angezeigt. Dafür müssen wir zwei Routen erstellen.

  • Zurück zur main.js. Zurerst importieren wir die neuen Kopmonenten nach den anderen Importen:
Copy
import Home from './views/Home';
import Pets from './views/Pets';
1
2
  • Jetzt erstellen wir die Routen. Jede Route ist ein Objekt, das einen Pfad und eine Komponente enthält. Füge die zwei Routen unter Vue.use... ein (eine ist für die Startseite, die andere für die Haustier-Galerie):
Copy
const routes = [
	{
		path: '/',
		component: Home,
	},
	{
		path: '/pets',
		component: Pets,
	},
];
1
2
3
4
5
6
7
8
9
10
  • Jetzt müssen wir eine VueRouter-Instanz erstellen und dieser unsere Routen übergeben. Kopiere diese Zeile unter das Objekt const routes (eine Zeile unter ];):
Copy
const router = new VueRouter({ routes });
1
  • Zum Schluss müssen wir den Router noch der Vue-App hinzufügen:
Copy
new Vue({
	vuetify,
	router,
	render: (h) => h(App),
}).$mount('#app');
1
2
3
4
5
  • Öffne jetzt die App.vue-Datei. Schreibe an die Stelle, an der vorher <div class="wrapper"> stand, den <router-view></router-view>-Tag. Er sollte zwischen dem Header und Footer stehen. Und nun wird in der App auch wieder etwas angezeigt!

Teste deinen Code. Füge /pets an das Ende der URL, jetzt kannst du die Haustier-Galerie sehen anstelle der Startseite.

Um das Wechseln zwischen den beiden Seiten einfacher zu machen, bauen wir eine Navigation ein. Dafür werden wir Vuetify nutzen, das wir bereits in Kapitel 1 hinzugefügt haben.

Die Toolbar-Komponente von Vuetify heißt v-toolbar. Kopiere sie in der App.vue direkt unter den h1-Tag in den Header:

Copy
<v-toolbar>
	<v-toolbar-items>
		<v-btn to="/" flat>Home</v-btn>
		<v-btn to="/pets" flat>Pets</v-btn>
	</v-toolbar-items>
</v-toolbar>
1
2
3
4
5
6

Du siehst jetzt zwei Buttons in dieser Toolbar. Jeder hat ein to-Attribut: Das ist ein router-link, der auf eine bestimmte Route zeigt. Jetzt können wir ganz einfach zwischen den Seiten wechseln, probiere es aus!

Ok, schön. Aber da sind noch keine Haustiere. Die bauen wir jetzt ein!

Daten erstellen

Wir werden zunächst ein paar Dummy-Daten hinzufügen. Erstelle dazu im src-Ordner einen neuen Ordner namens data und in diesem eine neue Datei namens dogs.js. Kopiere folgendes JSON-Objekt in die Datei:

Copy
export const Dogs = [
	{
		name: 'Max',
		breed: 'husky',
		img: 'https://images.dog.ceo/breeds/husky/n02110185_1469.jpg',
	},
	{
		name: 'Rusty',
		breed: 'shiba',
		img: 'https://images.dog.ceo/breeds/shiba/shiba-13.jpg',
	},
	{
		name: 'Rocco',
		breed: 'boxer',
		img: 'https://images.dog.ceo/breeds/boxer/n02108089_14112.jpg',
	},
	{
		name: 'Zoey',
		breed: 'beagle',
		img: 'https://images.dog.ceo/breeds/beagle/n02088364_11136.jpg',
	},
	{
		name: 'Duke',
		breed: 'doberman',
		img: 'https://images.dog.ceo/breeds/doberman/n02107142_4653.jpg',
	},
	{
		name: 'Lily',
		breed: 'malamute',
		img: 'https://images.dog.ceo/breeds/malamute/n02110063_1104.jpg',
	},
	{
		name: 'Winston',
		breed: 'pug',
		img: 'https://images.dog.ceo/breeds/pug/n02110958_15626.jpg',
	},
	{
		name: 'Angel',
		breed: 'samoyed',
		img: 'https://images.dog.ceo/breeds/samoyed/n02111889_4470.jpg',
	},
];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

Hier wird eine Konstante (const) names Dogs exportiert, die alle Daten beinhaltet, die wir benötigen.

  • Jetzt importieren wir diese Daten in die pets-Komponente. Öffne die Pets.vue-Datei und kopiere den folgenden <script> Block unter den <template>-Block.
Copy
<script>import {Dogs} from "../data/dogs";</script>
1

Dieser Teil importiert die Daten der Hunde. Jetzt müssen wir diese Daten der data()-Funktion hinzufügen. Bearbeite den <script> Block:

Copy
<script>
  import { Dogs } from "../data/dogs";
  export default {
    data() {
      return {
        dogs: Dogs
      };
    }
   };
  </script>
1
2
3
4
5
6
7
8
9
10

Dieses Skript stellt sicher, dass das dogs-Array ein Teil des Zustands ('state') der Pets-Komponente ist und im Template verwendet werden kann. Als nächstes werden wir unser Template erweitern, so dass es die Daten des dogs-Arrays anzeigt.

Die Daten in einer Liste ausgeben

Jetzt möchten wir eine Liste von Hunden erzeugen. Der einfachste Weg, um das zu erreichen, ist, über das Array zu iterieren und die Daten an eine Liste anzuhängen. Unsere dogs sind ein Array (=Liste von Objekten) und damit bereit verarbeitet zu werden. Um eine Liste von Einträgen darzustellen, die in einem Array stehen, gibt es in Vue die v-for Direktive.

Diese Direktive fügen wir dem v-flex-Element in Pets.vue hinzu:

Copy
<v-flex xs12 sm4 md3 v-for="pet in dogs" :key="pet.breed">...</v-flex>
1

Um korrekt über das Array zu iterieren und die Daten auszugeben, muss jedes Element ein eindeutiges Schlüsselattribut (=key attribute) haben. In unserem Fall wird die Art des Hundes dieses Schlüsselattribut sein.

Jetzt haben wir acht v-cards mit dem gleichen Text und Bild. Das stimmt so noch nicht.

In der v-for-Direktive wird der aktuelle Hund pet genannt.

💡

Wir haben diesen Namen in der Diretive zugewiesen; hätten wir geschrieben v-for="dog in dogs" würde das aktuelle Element dog heißen.

In der dog.js kannst du sehen, dass jeder Hund drei Eigenschaften hat: Name (name), Art (breed) und Bild (img). Das Bild können wir mit der v-img-Komponente anzeigen.

Wenn wir src nur mit dem Attriubut pet.img ersetzen...

Copy
<v-img src="pet.img" height="170px"></v-img>
1

... werden noch keine Bilder angezeigt. Warum? Weil wir so einen statischen Wert einsetzen, die App erwartet eine Datei mit dem Namen pet.img. Diese Datei gibt es allerdings nicht. Um den Wert von pet.img dynamisch in das src-Attribut zu setzen, müssen wir die v-bind-Direktive (oder den Shortcut :) nutzen.

Copy
<v-img :src="pet.img" height="170px"></v-img>
1

💡

Die v-bind-Direktive erzeugt dynamisch aus ein oder mehreren Attributen, oder sogar eine Komponenten-Variable einen JavaScript-Befehl. Den Unterschied macht der :!

Es funktioniert!

Als Nächstes möchten wir den Namen des Hundes anzeigen. Für Text wird in Vue die "mustache"-Syntax (=Schnauz) genutzt - doppelte geschweifte Klammern: {{ }}. Dieser Tag wird durch den Wert der zugewiesenen Eigenschaft ersetzt. Ersetze den Text Looking for a dog im <h3></h3> Tag, um den Namen des Hundes anzuzeigen:

Copy
<h3>{{pet.name}}</h3>
1

Jetzt fehlt noch die Art des Hundes. Füge einen weiteren <p></p> Tag unter der Zeile mit dem <h3></h3> Tag und zeige die Art des Hundes an:

Copy
<p>{{pet.breed}}</p>
1

Soweit funktioniert alles, wie wir es uns vorgestellt haben. Nur das Template ist inzwischen etwas unübersichtlich geworden. Wir können es überarbeiten und etwas verschlanken. Dafür erstellen wir eine Dog-Komponente und übergeben das aktuelle Haustier als Eigenschaft (=property).

💡

Eigenschaften (=properties) sind spezielle Attribute, die man einer Komponente zuweisen kann. Wenn ein Wert an ein Eigenschaftsattribut zugewiesen wird, wird dieser Wert eine Eigenschaft für diese eine Ausführung der Komponente. In unserem Fall wird die Dog-Komponente eine dog Property haben, die sie von der Eltern-Komponente Pets übergeben bekommt.

Überarbeitung des Templates - Property!

Erstelle einen neuen Ordner components im src-Ordner.

Erstelle eine neue Datei Dog.vue in dem components-Ordner und schreibe die <template></template> und <script></script>-Tags hinein. So sieht die neue Datei jetzt aus:

Copy
<template> </template>

<script></script>
1
2
3

Kopiere die gesamte v-card-Komponente aus Pets.vue in den <template>-Tag der Dogs-Komponente. Den Teil kannst du nun aus dem template in Pets.vue löschen.

Wie bereits erwähnt, benötigen wir eine dog-Property in der Dog-Komponente. Füge dazu die props zu der Komponente hinzu: Zuerst muss in den <script>-Tag eine Export-Anweisung; das ermöglicht uns später die Dogs-Komponente in der Pets-Komponente zu importieren und nutzen. Kopiere diesen <script>-Block nach Dogs.vue:

Copy
<script>export default {}</script>
1

Jetzt können wir die props und die Eigenschaft dog hinzufügen:

Copy
<script>
	export default {
	  props: {
	    dog: {
	      type: Object
	    }
	  }
	};
</script>
1
2
3
4
5
6
7
8
9

An dieser Stelle definieren wir auch den Typ der Eigenschaft - es soll ein JavaScript-Objekt sein.

In dem Template von Dog.vue musst du pet mit dog ersetzen, weil es innerhalb der Dog-Komponente keine pet-Variable gibt. So sollte das Template jetzt aussehen:

Copy
<template>
	<v-card color="grey lighten-2">
		<v-img :src="dog.img" height="170px"> </v-img>
		<v-card-title>
			<div>
				<h3>{{dog.name}}</h3>
				<p class="breed">{{dog.breed}}</p>
			</div>
		</v-card-title>
	</v-card>
</template>
1
2
3
4
5
6
7
8
9
10
11

In der Pets.vue-Datei müssen wir noch ein paar Änderungen machen. Zuerst importieren wir die neue Dog-Komponente; kopiere dazu diese Zeile unter den Import der Dogs-Komponente:

Copy
import Dog from '../components/Dog.vue';
1

Nun müssen wir der Pets-Komponente mitteilen, dass sie eine sogenannte Kind-Komponente beinhaltet. Vue nutzt die components-Option dafür; diese wird über data() deklariert:

Copy
export default {
	components: {
		appDog: Dog,
	},
	data() {
		return {
			dogs: Dogs,
		};
	},
};
1
2
3
4
5
6
7
8
9
10

💡

Jede Zuweisung in der components-Option besteht aus einem Schlüssel (=key) und einem Wert (=value). Der Key ist der Name des neuen Elementes und der Value beinhaltet options-Objekt für diese Komponente.

💡

Für den Namen der Komponente kannst du verschiedene Schreibweisen benutzen: camel-case (appDog) oder kebab-case ('app-dog'). Die Camel-case-Schreibweise wird im HTML-Tag in Kamel-Case "umgewandelt". Um die appDog-Komponente anzuzeigen, müssen wir den HTML-Tag <app-dog> nutzen und es wird das Template aus der Dog-Komponente angezeigt.

Füge den neuen <app-dog>-Tag in Pets.vue an die Stelle ein, an der du zuvor die <v-card> gelöscht hast.

Copy
<v-flex xs12 sm4 md3 v-for="pet in dogs" :key="pet.breed">
	<app-dog></app-dog>
</v-flex>
1
2
3

Jetzt müssen wir der Dog-Komponente noch die dog-Eigenschaft übergeben. Dafür nutzen wir wieder die v-bind-Direktive (oder den Shortcut :). Passe deinen gerade geschriebenen Code in Pets.vue entsprechend an:

Copy
<v-flex xs12 sm4 md3 v-for="pet in dogs" :key="pet.breed">
	<app-dog :dog="pet"></app-dog>
</v-flex>
1
2
3

Jetzt solltest du ein hübsches Kachel-Layout mit vielen Hunden haben! Kapitel 2 ist damit abgeschlossen!

Ergebnis

final result chapter 2