Skip to content

Vue.js

Projekt «WebApp»

Vue ist ein sogenanntes Javascript-Framework: Dieses wurde selbst in Javascript programmiert und läuft deshalb auf einer HTML-Seite im Browser. Mit Vue kann man interaktive WebApps programmieren.

Installation

Javascript laden

Wir können den von Vue verwendeten Javascript-Code direkt im HTML-Dokument verlinken. Der Browser lädt ihn so selbst vom Netz herunter. Dazu bauen wir im Head unserer HTML-Datei folgendes script-Element ein:

html
<script src="https://unpkg.com/vue@3"></script>

Vue-App initialisieren

Anschliessend muss die App initialisiert werden. Dazu ruft man Vue.createApp() (mit entsprechenden Argumenten) auf und verbindet die App mit einem HTML-Element über die mount-Methode:

javascript
Vue.createApp().mount('#app')
html
<div id="app"></div>

Variablen

Vue definiert in der data()-Funktion reaktive Variablen und Objekte. Diese können im HTML-Teil mit Hilfe einer Template-Sprache mit geschweiften Klammern dargestellt werden oder mit v-model an Eingabe-Elemente gebunden werden.

html
<div id="app">
	Dein Name: <input type="text" v-model="name" label="Name" />
	Hello {{ name }}
</div>
javascript
Vue.createApp({
	data() {
		return {
			name: 'Vue',
		}
	}	
}).mount('#app')

Hello World

Diese Hello-World-Vue-App fasst das oben beschriebene zusammen:

html
<!DOCTYPE html>
<html lang="de">
	<head>
		<title>Hello Vue</title>
		<script src="https://unpkg.com/vue@3"></script>
	</head>
	<body>
		<div id="app">
			<input type="text" v-model="name" />
			Hello {{ name }}
		</div>
		<script>
  		Vue.createApp({
			data() {
	  		return {
				name: 'Vue',
	  		}
			}	
  		}).mount('#app')
		</script>
	</body>
</html>

Dein Name:

Hello Vue

Aufgabe

Teste das obenstehende Beispiel aus. Versuche weitere Variablen einzubauen.

Templates

Vue-Templates beinhalten HTML-Code mit speziellen Platzhaltern und Anweisungen für Vue. Es können so Werte angezeigt werden, Elemente bedingt angezeigt werden und auf Events reagiert werden:

Variablen ausgeben

Werte von Variablen können in doppelt geschweiften Klammern ausgegeben werden.

html
<p>Suche nach {{ search }}</p>

In der Klammer drin sind auch einfache Javascript-Ausdrücke möglich, z.B. mit dem «bedingten Operator» – sozusagen ein abgekürztes if-else – um etwas anzuzeigen sollte die Variable search leer sein:

html
<p>Suche nach {{ search ? search : "–" }}</p>

Wir können das lesen wie folgt: «ist search gesetzt? Dann den Wert von search ausgeben, sonst einen Bindestrich ausgeben»

Variablen ändern

Man kann Vue-Variablen mittels v-model an HTML-Input-Elemente knüpfen:

html
<input type="text" v-model="search" />

Das Text-Feld zeigt den Wert von search an – aber man kann ihn damit auch ändern.

Beispiel Variablen

Suche:

Suche nach –

Listen

Listen von Elementen brauchen eine Wiederholung zur Darstellung. In Vue verwendet man dafür die v-for-Direktive. Das für die Direktive verwendete HTML-Element wird für jeden Eintrag in der Liste dargestellt. Im Element drin kann man auf das Element der Liste zugreifen:

html
<div v-for="movie in movies">
	<h1>{{ movie.title }}</h1>
	<p>{{ movie.year }}</p>
</div>
Zeile 1
das div-Element wir für jeden movie aus der Liste movies wiederholt
Zeile 2 & 3
movie ist ein Objekt aus der Liste movies
mit der Punktnotation können wir auf die einzelnen Eigenschaften zugreifen
Zeile 4
Das div-Element – und somit auch der Zugriff auf movie – wird beendet

if-elseif-else

Ein HTML-Element kann mit der v-if-Direktive bedingt gerendert werden. Der Ausdruck wird ausgewertet und das HTML-Element nur dargestellt, wenn die Auswertung true ergibt. Das direkt nachkommende HTML-Element kann mit einer v-else-Direktive versehen werden.

html
<div v-for="movie in movies">
	<h1>{{ movie.title }}</h1>
	<p v-if="movie.year > 2021">Neu!!</p>
	<p v-else>{{ movie.year }}</p>
</div>

Seit Vue 3.2 sind zwischen v-if und v-else ein oder mehrere Elemente mit v-elseif möglich.

Ereignisse

Ereignisse verwenden wir meistens wenn die Maus etwas anklickt. Dazu können wir die @click-Direktive verwenden:

html
<div v-for="movie in movies" @click="showDetails(movie.id)">
	<h1>{{ movie.title }}</h1>
	<p>{{ movie.year }}</p>
</div>
Zeile 1
bei jedem Film binden wir das Klick-Event auf die Methode showDetails und übergeben dieser die ID des Film. Diese sollte dann Details zum Film anzeigen.

Methoden

Die Vue-App kann Methoden definieren die wir dann entweder als Ereignis oder automatisch aufrufen können:

javascript
Vue.createApp({
	data() {
		return {
			name: 'Vue',
			movies: []
		}
	},
	methods: {
		async getMovies() {
			const response = await fetch("https://informatik.mygymer.ch/fts/rest/movies/");
			const json = await response.json();
			this.movies = json.movies;
		},
		showDetails(id) {
			// fetch movie-Details and show in App
		}
	}
}).mount('#app')

Hooks

Mit den sogenannten Hooks kann man sozusagen im Vue-Programm-Ablauf «ein-haken». So wird z.B. der created-Hook aufgerufen, sobald die Vue-App erzeugt wurde. Er lässt sich praktisch verwenden um erste Daten zu laden:

javascript
Vue.createApp({
	data() {
		return {
			name: 'Vue',
			movies: []
		}
	},
	methods: {
		async getMovies() {
			const response = await fetch("https://informatik.mygymer.ch/fts/rest/movies/");
			const json = await response.json();
			this.movies = json.movies;
		},
		showDetails(id) {
			// fetch movie-Details and show in App
		}
	},
	created() {
		this.getMovies()
	}	
}).mount('#app')

Komplettes Beispiel

html
<!DOCTYPE html>
<html>
	<head>
		<title>Movies</title>
		<script src="https://unpkg.com/vue@3"></script>
		<link rel="stylesheet" type="text/css" href="style.css" />
	</head>
<body>
<div id="app">
	<header><h1>@theMovies</h1></header>
	<article v-if="movie">
		<ul>
			<li v-on:click="movie = false" class="clickable">			
				<p>zurück zur Liste</p>				
			</li>				
			<li>
				<img :src="'https://informatik.mygymer.ch/fts/rest/media/movies/'+movie.id" />				
			</li>	
			<li>							
				<h1>{{movie.title}}</h1>
				<p>erschienen: {{movie.year}}</p>
				<p v-if="movie.actors && movie.actors.length > 0">{{movie.actors.length}} Schauspieler:innen erfasst</p>
				<p v-else>keine Schauspieler:innen erfasst</p>
			</li>											
		</ul>			
		<ul>				
			<li v-for="actor in movie.actors">
				<img :src="'https://informatik.mygymer.ch/fts/rest/media/actors/'+actor.id" />
				<p>{{actor.name}}<br>{{actor.as_character}}</p>
			</li>
		</ul>		
	</article>
	<article v-else>
		<ul>
			<li v-for="movie  in movies" v-on:click="getMovie(movie.id)" class="clickable">
				<img :src="'https://informatik.mygymer.ch/fts/rest/media/movies/'+movie.id" />
				<p>{{movie.title}}</p>
			</li>
		</ul>
	</article>	
	<footer></footer>
</div>
<script>
  Vue.createApp({
	data() {
	  return {
		movies: [],
		movie: false
	  }
	},
	methods: {
		async getMovies() {
			this.movies = [];
			const response  = await fetch("https://informatik.mygymer.ch/fts/rest/movies/");
			const json = await response.json();
			this.movies = json.movies;
		},		
		async getMovie(id) {
			this.movie = false;
			const response  = await fetch("https://informatik.mygymer.ch/fts/rest/movies/"+id);
			const json = await response.json();
			this.movie = json;
		}
	},
	created() {
		this.getMovies()
	}
  }).mount('#app')
</script>
</body>
</html>
css
/* https://www.colourlovers.com/palette/482774/dream_magnet*/

body {
  background-color: #343838;
  font-family: system-ui;
  color: #00b4cc;
  margin: 0;
  padding: 0;
}
header {
  background-color: #008c9e;
  color: #343838;
}
ul {
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}
li {
  min-width: 30%;
  width: 200px;
  margin: 10px;
  list-style: none;
}
img {
  width: 100%;
  border: 1px solid #00b4cc;
}
li.clickable {
  cursor: pointer;
}
li.clickable:hover {
  color: #00dffc;
}
li.clickable img:hover {
  border: 1px solid #00dffc;
}

article {
  margin: 10px;
}
  • Mit dem created-Hook wird die Methode getMovies() aufgerufen.
  • Diese lädt mit fetch eine Liste von Filmen
  • diese Filme werden im Template angezeigt
  • klickt man auf einen Film, so wird die Methode getMovie ausgeführt
  • diese holt über die id Details (Liste der Schauspieler:innen) zu diesem Film und speichert diese in der Variablen movie
  • die Ansicht ändert nun

Das Ganze sieht wie folgt aus:

@theMovies

mehrere Seiten

Ev. lohnt es sich, die WebApp auf mehrere Dokumente aufzuteilen. Z.B. eine Suchseite search.html und eine Detail-Seite movie.html für den Film.
Damit die Detail-Seite aber weiss, um welchen Film es sich handelt, müssen wir beim Aufruf die Film-ID mitliefern. Dies können wir an die URL anhängen.

Querystring

Der Querystring wird durch ein Fragezeichen an die URL angehängt. Er beschreibt sogenannte Key/Value-Paare. Möchten wir z.B. die Datei movie.html aufrufen und als id den Wert 84 mitgeben würde die URL wie folgt aussehen:

movie.html?id=84

Wir können auch mehrere Key/Value-Paare mitliefern. Diese werden dann durch & getrennt:

movie.html?id=84&language=en

Querystring-Parameter auslesen

Rufen wir eine Seite mit einem Querystring auf, dann können wir die Parameter mit Javascript auslesen.

Wenn wir also z.B. suche.html?q=Titanic aufrufen, dann kann die Seite suche.html mit Javascript auf den Querystring zugreifen. Dies geschieht in drei Schritten:

javascript
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const search = urlParams.get('q');
Zeile 1
wir holen den Querystring über die Eigenschaft search der URL des aktuellen Fensters
dies ergibt im Beispiel den String q=Curry
Zeile 2
mit URLSearchParams können wir diesen String parsen, d.h. es wird überprüft, ob es sich um korrekte Parameter handelt und welche Datentypen dabei vorkommen.
Zeile 3
mit der Methode get() von URLSearchParams können nun Werte für einzelne Elemente abgerufen werden. Wenn wir also den Wert für den Paramater q abfragen, erhalen wir nun den vom Beuntzer eingegebenen Suchbegriff Titanic zurück.

Beispiel

Das selbe Beispiel wie oben aber auf zwei Seiten aufgeteilt:

html
<!DOCTYPE html>
<html>
	<head>
		<title>Movies</title>
		<script src="https://unpkg.com/vue@3"></script>
		<link rel="stylesheet" type="text/css" href="style.css" />
	</head>
<body>
<div id="app">
	<header><h1>@theMovies</h1></header>
	<article>
		<ul>
			<li v-for="movie in movies">
				<a :href="'movie.html?id=' + movie.id">
					<img :src="'https://informatik.mygymer.ch/fts/rest/media/movies/'+movie.id" />
				</a>
				<p>{{movie.title}}</p>				
			</li>
		</ul>
	</article>	
	<footer></footer>
</div>
<script>
  Vue.createApp({
	data() {
	  return {
		movies: [],
	  }
	},
	methods: {
		async getMovies() {
			this.movies = [];
			const response  = await fetch("https://informatik.mygymer.ch/fts/rest/movies/");
			const json = await response.json();
			this.movies = json.movies;
		},		
	},
	created() {
		this.getMovies()
	}
  }).mount('#app')
</script>
</body>
</html>
html
<!DOCTYPE html>
<html>
	<head>
		<title>Movies</title>
		<script src="https://unpkg.com/vue@3"></script>
		<link rel="stylesheet" type="text/css" href="style.css" />
	</head>
<body>
<div id="app">
	<header><h1>@theMovies</h1></header>
	<article>
		<ul>
			<li>			
				<a href="search.html">zurück zur Liste</a>				
			</li>				
			<li>
				<img :src="'https://informatik.mygymer.ch/fts/rest/media/movies/'+movie.id" />				
			</li>	
			<li>							
				<h1>{{movie.title}}</h1>
				<p>erschienen: {{movie.year}}</p>
				<p v-if="movie.actors && movie.actors.length > 0">{{movie.actors.length}} Schauspieler:innen erfasst</p>
				<p v-else>keine Schauspieler:innen erfasst</p>
			</li>											
		</ul>			
		<ul>				
			<li v-for="actor in movie.actors">
				<img :src="'https://informatik.mygymer.ch/fts/rest/media/actors/'+actor.id" />
				<p>{{actor.name}}<br>{{actor.as_character}}</p>
			</li>
		</ul>		
	</article>
	<footer></footer>
</div>
<script>
  Vue.createApp({
	data() {
	  return {
		movie: false
	  }
	},
	methods: {	
		async getMovie(id) {
			this.movie = false;
			const response  = await fetch("https://informatik.mygymer.ch/fts/rest/movies/"+id);
			const json = await response.json();
			this.movie = json;
		}
	},
	created() {
		const queryString = window.location.search;
		const urlParams = new URLSearchParams(queryString);
		const id = urlParams.get('id');
		this.getMovie(id)
	}
  }).mount('#app')
</script>
</body>
</html>
css
/* https://www.colourlovers.com/palette/482774/dream_magnet*/

body {
  background-color: #343838;
  font-family: system-ui;
  color: #00b4cc;
  margin: 0;
  padding: 0;
}
header {
  background-color: #008c9e;
  color: #343838;
}
ul {
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}
li {
  min-width: 30%;
  width: 200px;
  margin: 10px;
  list-style: none;
}
img {
  width: 100%;
  border: 1px solid #00b4cc;
}
li.clickable {
  cursor: pointer;
}
li.clickable:hover {
  color: #00dffc;
}
li.clickable img:hover {
  border: 1px solid #00dffc;
}

article {
  margin: 10px;
}

Gymnasium Kirchenfeld, fts & lem