Übungen¶
Übung 0¶
Infrastruktur einrichten
- wählen Sie eine IDE aus und installieren Sie diese
- richten Sie sich ein Git-Repository ein (z.B.
WebTech23) - erstellen Sie im Ordner
WebTech23eineindex.htmlund versuchen Sie darin bereits einige Inhalte einzupflegen, z.B. eine kleine persönliche Webseite oder eine Startseite von der aus alle Übungen erreichbar sind o.ä. - commiten und pushen Sie Ihr Repository auf einen zentralen Dienst (siehe)
- laden Sie mich zu Ihrem Git-Dienst ein (siehe)
Übung 1¶
Übungsaufgabe 1 (HTML)
- Erstellen Sie in einem
Uebung1-Ordner eine Dateiuebung1.html. Dasbody-Element soll einheader-Element, einnav-Element, einsection-Element und einfooter-Element enthalten. -
Unter dieser Übungsaufgabe (siehe
mockupdata) ist der HTML-Code einer Tabelle mit allen Teilnehmerinnen einer Veranstaltung. Kopieren Sie den Inhalt der Datei so in Ihren HTML-Code, dass folgende Seite erscheint:
-
Es sollen 4 Unterseiten erstellt werden. Bei Klick auf diese Seiten soll die Tabelle jeweils nur die Teilnehmerinnen enthalten, deren Nachname mit dem entsprechenden Anfangsbuchstaben beginnt (also in
ag.htmlnur alle Nachnamen, die mit A bis G beginnen). Die Seitenag.html,hl.html,mr.htmlundsz.htmlsollen im OrdnerNNabgelegt werden, der Unterordner vonUebung1ist. - Achten Sie darauf, dass man von jeder Unterseite auf jede andere Unterseite und auch auf die Hauptseite (
uebung1.html) wechseln können muss. - Das einzubindende Logo des Studiengangs liegt hier. Es soll in einen
images-Ordner gespeichert werden, der in der Ordner-Hierarchie neben demUebung1-Ordner liegt. Um die Größe des Bildes festzulegen, können Sie mit Hilfe desstyle-Attributes die Höhe und die Breite bestimmen:style="width:53px; height:48px;" - Nächste Woche wird Uebung1 um CSS erweitert.
mockupdata
<table>
<thead>
<tr>
<th>Vorname</th>
<th>Nachname</th>
<th>E-Mail-Adresse</th>
<th>IP-Adresse</th>
</tr>
</thead>
<tbody>
<tr>
<td>Adam</td>
<td>Anderson</td>
<td>aanderson8@google.fr</td>
<td>118.93.83.157</td>
</tr>
<tr>
<td>Susan</td>
<td>Andrews</td>
<td>sandrewsn@google.co.jp</td>
<td>228.214.9.251</td>
</tr>
<tr>
<td>Catherine</td>
<td>Andrews</td>
<td>candrewsp@noaa.gov</td>
<td>112.111.87.178</td>
</tr>
<tr>
<td>Alan</td>
<td>Bradley</td>
<td>abradley1c@globo.com</td>
<td>229.152.117.127</td>
</tr>
<tr>
<td>Anne</td>
<td>Brooks</td>
<td>abrooks16@bravesites.com</td>
<td>243.159.39.234</td>
</tr>
<tr>
<td>Russell</td>
<td>Brown</td>
<td>rbrownq@nifty.com</td>
<td>215.38.120.242</td>
</tr>
<tr>
<td>Ryan</td>
<td>Burton</td>
<td>rburton18@foxnews.com</td>
<td>159.60.107.14</td>
</tr>
<tr>
<td>Roy</td>
<td>Campbell</td>
<td>rcampbell1@geocities.com</td>
<td>237.232.34.20</td>
</tr>
<tr>
<td>Russell</td>
<td>Campbell</td>
<td>rcampbell17@eventbrite.com</td>
<td>251.2.92.63</td>
</tr>
<tr>
<td>Bonnie</td>
<td>Coleman</td>
<td>bcoleman11@fc2.com</td>
<td>109.150.122.102</td>
</tr>
<tr>
<td>Ernest</td>
<td>Coleman</td>
<td>ecoleman15@businessweek.com</td>
<td>213.173.4.7</td>
</tr>
<tr>
<td>Richard</td>
<td>Cruz</td>
<td>rcruz7@unc.edu</td>
<td>235.124.23.221</td>
</tr>
<tr>
<td>Sean</td>
<td>Cruz</td>
<td>scruz10@answers.com</td>
<td>92.255.49.227</td>
</tr>
<tr>
<td>Rebecca</td>
<td>Cunningham</td>
<td>rcunninghamd@mac.com</td>
<td>65.79.191.52</td>
</tr>
<tr>
<td>Margaret</td>
<td>Evans</td>
<td>mevansh@pcworld.com</td>
<td>162.10.86.196</td>
</tr>
<tr>
<td>Jeffrey</td>
<td>Ford</td>
<td>jford14@cnet.com</td>
<td>210.216.54.14</td>
</tr>
<tr>
<td>Andrea</td>
<td>Gardner</td>
<td>agardnerv@woothemes.com</td>
<td>179.91.0.30</td>
</tr>
<tr>
<td>Deborah</td>
<td>George</td>
<td>dgeorge6@furl.net</td>
<td>201.76.47.162</td>
</tr>
<tr>
<td>Sean</td>
<td>Gibson</td>
<td>sgibsony@alexa.com</td>
<td>48.114.103.55</td>
</tr>
<tr>
<td>Virginia</td>
<td>Graham</td>
<td>vgrahamk@aol.com</td>
<td>165.219.171.1</td>
</tr>
<tr>
<td>Steven</td>
<td>Hamilton</td>
<td>shamiltonu@state.tx.us</td>
<td>38.194.91.201</td>
</tr>
<tr>
<td>Virginia</td>
<td>Hawkins</td>
<td>vhawkinsf@ehow.com</td>
<td>93.120.46.203</td>
</tr>
<tr>
<td>Edward</td>
<td>Hicks</td>
<td>ehicksc@pcworld.com</td>
<td>199.153.27.1</td>
</tr>
<tr>
<td>Mark</td>
<td>Johnson</td>
<td>mjohnsonj@hostgator.com</td>
<td>73.87.135.206</td>
</tr>
<tr>
<td>Ruth</td>
<td>Jordan</td>
<td>rjordan1a@smugmug.com</td>
<td>193.140.80.64</td>
</tr>
<tr>
<td>Antonio</td>
<td>Kim</td>
<td>akim4@odnoklassniki.ru</td>
<td>168.244.191.78</td>
</tr>
<tr>
<td>Jennifer</td>
<td>Marshall</td>
<td>jmarshallt@gnu.org</td>
<td>104.191.49.94</td>
</tr>
<tr>
<td>Eric</td>
<td>Matthews</td>
<td>ematthews5@independent.co.uk</td>
<td>138.194.30.1</td>
</tr>
<tr>
<td>Raymond</td>
<td>Mcdonald</td>
<td>rmcdonald2@ihg.com</td>
<td>161.24.42.24</td>
</tr>
<tr>
<td>Eric</td>
<td>Miller</td>
<td>emillere@creativecommons.org</td>
<td>122.159.17.218</td>
</tr>
<tr>
<td>Jonathan</td>
<td>Morales</td>
<td>jmoralesa@ovh.net</td>
<td>97.65.110.105</td>
</tr>
<tr>
<td>Marie</td>
<td>Morgan</td>
<td>mmorganb@cloudflare.com</td>
<td>226.79.152.112</td>
</tr>
<tr>
<td>Amanda</td>
<td>Nelson</td>
<td>anelson13@indiatimes.com</td>
<td>161.185.121.245</td>
</tr>
<tr>
<td>Lisa</td>
<td>Olson</td>
<td>lolsonr@telegraph.co.uk</td>
<td>77.245.172.100</td>
</tr>
<tr>
<td>Alice</td>
<td>Ortiz</td>
<td>aortizw@histats.com</td>
<td>179.52.222.21</td>
</tr>
<tr>
<td>Peter</td>
<td>Phillips</td>
<td>pphillipss@1688.com</td>
<td>11.158.255.76</td>
</tr>
<tr>
<td>Matthew</td>
<td>Porter</td>
<td>mporter9@europa.eu</td>
<td>174.81.178.88</td>
</tr>
<tr>
<td>Tammy</td>
<td>Ray</td>
<td>trayx@weather.com</td>
<td>192.243.38.190</td>
</tr>
<tr>
<td>Mark</td>
<td>Richardson</td>
<td>mrichardson1d@ihg.com</td>
<td>209.217.14.154</td>
</tr>
<tr>
<td>Joan</td>
<td>Roberts</td>
<td>jroberts12@alibaba.com</td>
<td>4.91.143.62</td>
</tr>
<tr>
<td>Kathleen</td>
<td>Rose</td>
<td>kroseg@pinterest.com</td>
<td>222.172.140.56</td>
</tr>
<tr>
<td>Steve</td>
<td>Sanders</td>
<td>ssanders1b@wikispaces.com</td>
<td>91.61.109.245</td>
</tr>
<tr>
<td>Shirley</td>
<td>Scott</td>
<td>sscottm@macromedia.com</td>
<td>219.237.108.82</td>
</tr>
<tr>
<td>Lillian</td>
<td>Stephens</td>
<td>lstephens19@hugedomains.com</td>
<td>89.85.137.204</td>
</tr>
<tr>
<td>Nicole</td>
<td>Thompson</td>
<td>nthompson3@admin.ch</td>
<td>13.183.208.155</td>
</tr>
<tr>
<td>Marie</td>
<td>Thompson</td>
<td>mthompsonz@yelp.com</td>
<td>162.164.5.231</td>
</tr>
<tr>
<td>Alan</td>
<td>Vasquez</td>
<td>avasquezo@miibeian.gov.cn</td>
<td>178.109.86.172</td>
</tr>
<tr>
<td>Mildred</td>
<td>Watkins</td>
<td>mwatkins0@miibeian.gov.cn</td>
<td>150.67.132.64</td>
</tr>
<tr>
<td>Eugene</td>
<td>Williams</td>
<td>ewilliamsi@deliciousdays.com</td>
<td>67.208.26.182</td>
</tr>
<tr>
<td>Catherine</td>
<td>Williams</td>
<td>cwilliamsl@360.cn</td>
<td>154.87.204.51</td>
</tr>
</tbody>
</table>
Übung 2¶
Übungsaufgabe 2 (CSS)
- Erstellen Sie (falls noch nicht geschehen) eine
index.html-Datei in Ihrem Repository-Ordner (also z.B.DocumentRoot/Webtech22) derart, dass diese direkte Links auf Ihre Lösungen der Übungen enthalten (z.B. in einer Tabelle oder einer Liste). - Kopieren Sie den Ordner
Uebung1in den OrdnerUebung2(also inkl. OrdnerNN). Ändern Sie darin die Dateiuebung1.htmlzuuebung2.html(umbenennen). Ändern Sie entsprechend alle Links innerhalb der Datei, damit Sie z.B. auf den OrdnerUebung2/NNzugreifen und nicht mehr aufUebung1/NN. - Legen Sie sich im
Uebung2-Ordner einen Ordnerstylesan. Erstellen Sie in demstyles-Ordner eine Dateimystyles.css. - Fügen Sie im
<head>-Bereich deruebung2.htmleine logische Verknüpfung zurmystyles.css-Datei ein (<link href="./styles/mystyles.css" rel="stylesheet">). - In
mystyles.cssdefinieren Sie (versuchen Sie mind. 6 der genannten 8 Punkte umzusetzen):- Verdana als Schriftart für das ganze Dokument
- der
<header>soll das HTW-Grau als Hintergrundfarbe, HTW-Orange als Schriftfarbe und der Text zentriert sein (siehe HTW Corporate Design). - die Tabelle soll ungefähr wie dargestellt aussehen:
- d.h. mit Rahmen, mit einem Schatten, die Spaltenüberschriften in weiß, mit HTW-grünem Hintergrund.
- Das
<nav>-Feld hat einen Abstand nach oben und nach unten von jeweils50px. <a>-Elemente in einem<nav>-Element haben einen grauen Rand, deren Ecken abgerundet sind. Die Hintergrundfarbe ist HTW-blau. Schriftart ist weiß und bold.<a>-Elemente in einem<nav>-Element sind nicht unterstrichen!- Fährt man mit der Maus über ein
<a>-Element in einem<nav>-Element ändert sich die Hintergrundfarbe in HTW-grau und die Schriftfarbe in HTW-orange.
- Die Tabelle hat eine Breite von 80% des
<section>-Elements. - Der
<footer>ist in HTW-Orange, die Schrift ist schwarz, der Text ist mittig und die Schrift ist kleiner. Die Höhe des footers entsteht dadurch, dass die Schrift einen Abstand nach oben und nach unten von jeweils20pxhat.
- Am Ende soll es ungefähr so aussehen:
Eine mögliche Lösung für Übung 2
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Uebung 2</title>
<link href="./styles/mystyles.css" rel="stylesheet">
</head>
<body>
<header>
<h1>Teilnehmerinnen Webtech</h1>
<img src="../images/fiw.jpg" alt="FIW Logo" style="width:53px; height: 48px;">
</header>
<nav>
<a href="#">ALLE</a>
<a href="NN/ag.html" title="Alle Nachnamen A-G">A-G</a>
<a href="NN/hl.html">H-L</a>
<a href="NN/mr.html">M-R</a>
<a href="NN/sz.html">S-Z</a>
<a href="../index.html">Startseite</a>
</nav>
<section>
<table>
<thead>
<tr>
<th>Vorname</th>
<th>Nachname</th>
<th>E-Mail-Adresse</th>
<th>IP-Adresse</th>
</tr>
</thead>
<tbody>
<tr>
<td>Adam</td>
<td>Anderson</td>
<td>aanderson8@google.fr</td>
<td>118.93.83.157</td>
</tr>
<tr>
<td>Susan</td>
<td>Andrews</td>
<td>sandrewsn@google.co.jp</td>
<td>228.214.9.251</td>
</tr>
<tr>
<td>Catherine</td>
<td>Andrews</td>
<td>candrewsp@noaa.gov</td>
<td>112.111.87.178</td>
</tr>
<tr>
<td>Alan</td>
<td>Bradley</td>
<td>abradley1c@globo.com</td>
<td>229.152.117.127</td>
</tr>
<tr>
<td>Anne</td>
<td>Brooks</td>
<td>abrooks16@bravesites.com</td>
<td>243.159.39.234</td>
</tr>
<tr>
<td>Russell</td>
<td>Brown</td>
<td>rbrownq@nifty.com</td>
<td>215.38.120.242</td>
</tr>
<tr>
<td>Ryan</td>
<td>Burton</td>
<td>rburton18@foxnews.com</td>
<td>159.60.107.14</td>
</tr>
<tr>
<td>Roy</td>
<td>Campbell</td>
<td>rcampbell1@geocities.com</td>
<td>237.232.34.20</td>
</tr>
<tr>
<td>Russell</td>
<td>Campbell</td>
<td>rcampbell17@eventbrite.com</td>
<td>251.2.92.63</td>
</tr>
<tr>
<td>Bonnie</td>
<td>Coleman</td>
<td>bcoleman11@fc2.com</td>
<td>109.150.122.102</td>
</tr>
<tr>
<td>Ernest</td>
<td>Coleman</td>
<td>ecoleman15@businessweek.com</td>
<td>213.173.4.7</td>
</tr>
<tr>
<td>Richard</td>
<td>Cruz</td>
<td>rcruz7@unc.edu</td>
<td>235.124.23.221</td>
</tr>
<tr>
<td>Sean</td>
<td>Cruz</td>
<td>scruz10@answers.com</td>
<td>92.255.49.227</td>
</tr>
<tr>
<td>Rebecca</td>
<td>Cunningham</td>
<td>rcunninghamd@mac.com</td>
<td>65.79.191.52</td>
</tr>
<tr>
<td>Margaret</td>
<td>Evans</td>
<td>mevansh@pcworld.com</td>
<td>162.10.86.196</td>
</tr>
<tr>
<td>Jeffrey</td>
<td>Ford</td>
<td>jford14@cnet.com</td>
<td>210.216.54.14</td>
</tr>
<tr>
<td>Andrea</td>
<td>Gardner</td>
<td>agardnerv@woothemes.com</td>
<td>179.91.0.30</td>
</tr>
<tr>
<td>Deborah</td>
<td>George</td>
<td>dgeorge6@furl.net</td>
<td>201.76.47.162</td>
</tr>
<tr>
<td>Sean</td>
<td>Gibson</td>
<td>sgibsony@alexa.com</td>
<td>48.114.103.55</td>
</tr>
<tr>
<td>Virginia</td>
<td>Graham</td>
<td>vgrahamk@aol.com</td>
<td>165.219.171.1</td>
</tr>
<tr>
<td>Steven</td>
<td>Hamilton</td>
<td>shamiltonu@state.tx.us</td>
<td>38.194.91.201</td>
</tr>
<tr>
<td>Virginia</td>
<td>Hawkins</td>
<td>vhawkinsf@ehow.com</td>
<td>93.120.46.203</td>
</tr>
<tr>
<td>Edward</td>
<td>Hicks</td>
<td>ehicksc@pcworld.com</td>
<td>199.153.27.1</td>
</tr>
<tr>
<td>Mark</td>
<td>Johnson</td>
<td>mjohnsonj@hostgator.com</td>
<td>73.87.135.206</td>
</tr>
<tr>
<td>Ruth</td>
<td>Jordan</td>
<td>rjordan1a@smugmug.com</td>
<td>193.140.80.64</td>
</tr>
<tr>
<td>Antonio</td>
<td>Kim</td>
<td>akim4@odnoklassniki.ru</td>
<td>168.244.191.78</td>
</tr>
<tr>
<td>Jennifer</td>
<td>Marshall</td>
<td>jmarshallt@gnu.org</td>
<td>104.191.49.94</td>
</tr>
<tr>
<td>Eric</td>
<td>Matthews</td>
<td>ematthews5@independent.co.uk</td>
<td>138.194.30.1</td>
</tr>
<tr>
<td>Raymond</td>
<td>Mcdonald</td>
<td>rmcdonald2@ihg.com</td>
<td>161.24.42.24</td>
</tr>
<tr>
<td>Eric</td>
<td>Miller</td>
<td>emillere@creativecommons.org</td>
<td>122.159.17.218</td>
</tr>
<tr>
<td>Jonathan</td>
<td>Morales</td>
<td>jmoralesa@ovh.net</td>
<td>97.65.110.105</td>
</tr>
<tr>
<td>Marie</td>
<td>Morgan</td>
<td>mmorganb@cloudflare.com</td>
<td>226.79.152.112</td>
</tr>
<tr>
<td>Amanda</td>
<td>Nelson</td>
<td>anelson13@indiatimes.com</td>
<td>161.185.121.245</td>
</tr>
<tr>
<td>Lisa</td>
<td>Olson</td>
<td>lolsonr@telegraph.co.uk</td>
<td>77.245.172.100</td>
</tr>
<tr>
<td>Alice</td>
<td>Ortiz</td>
<td>aortizw@histats.com</td>
<td>179.52.222.21</td>
</tr>
<tr>
<td>Peter</td>
<td>Phillips</td>
<td>pphillipss@1688.com</td>
<td>11.158.255.76</td>
</tr>
<tr>
<td>Matthew</td>
<td>Porter</td>
<td>mporter9@europa.eu</td>
<td>174.81.178.88</td>
</tr>
<tr>
<td>Tammy</td>
<td>Ray</td>
<td>trayx@weather.com</td>
<td>192.243.38.190</td>
</tr>
<tr>
<td>Mark</td>
<td>Richardson</td>
<td>mrichardson1d@ihg.com</td>
<td>209.217.14.154</td>
</tr>
<tr>
<td>Joan</td>
<td>Roberts</td>
<td>jroberts12@alibaba.com</td>
<td>4.91.143.62</td>
</tr>
<tr>
<td>Kathleen</td>
<td>Rose</td>
<td>kroseg@pinterest.com</td>
<td>222.172.140.56</td>
</tr>
<tr>
<td>Steve</td>
<td>Sanders</td>
<td>ssanders1b@wikispaces.com</td>
<td>91.61.109.245</td>
</tr>
<tr>
<td>Shirley</td>
<td>Scott</td>
<td>sscottm@macromedia.com</td>
<td>219.237.108.82</td>
</tr>
<tr>
<td>Lillian</td>
<td>Stephens</td>
<td>lstephens19@hugedomains.com</td>
<td>89.85.137.204</td>
</tr>
<tr>
<td>Nicole</td>
<td>Thompson</td>
<td>nthompson3@admin.ch</td>
<td>13.183.208.155</td>
</tr>
<tr>
<td>Marie</td>
<td>Thompson</td>
<td>mthompsonz@yelp.com</td>
<td>162.164.5.231</td>
</tr>
<tr>
<td>Alan</td>
<td>Vasquez</td>
<td>avasquezo@miibeian.gov.cn</td>
<td>178.109.86.172</td>
</tr>
<tr>
<td>Mildred</td>
<td>Watkins</td>
<td>mwatkins0@miibeian.gov.cn</td>
<td>150.67.132.64</td>
</tr>
<tr>
<td>Eugene</td>
<td>Williams</td>
<td>ewilliamsi@deliciousdays.com</td>
<td>67.208.26.182</td>
</tr>
<tr>
<td>Catherine</td>
<td>Williams</td>
<td>cwilliamsl@360.cn</td>
<td>154.87.204.51</td>
</tr>
</tbody>
</table>
</section>
<footer>
Jörn Freiheit - Übung 2
</footer>
</body>
</html>
body {
font-family: Verdana;
}
header {
background-color: #AFAFAF;
color: #FF5F00;
text-align: center;
}
nav {
margin-top: 3%;
margin-bottom: 3%;
}
table {
border-collapse: collapse;
width: 80%;
box-shadow: 10px 10px 5px grey;
}
table thead {
background-color: #76B900;
color: white;
}
table td,
table th {
border: 1px solid #ddd;
padding: 8px;
}
table tr:nth-child(even) {
background-color: #f2f2f2;
}
table tr:hover {
background-color: #ddd;
}
nav a {
text-decoration: none;
border: 1px solid #0082D1;
background-color: #0082D1;
border-radius: 7px;
color: white;
font-weight: bold;
text-align: center;
padding: 10px;
box-shadow: 5px 5px 5px grey;
}
nav a:hover {
border: 1px solid #AFAFAF;
background-color: #AFAFAF;
color: #FF5F00;
}
footer {
background-color: #FF5F00;
color: black;
text-align: center;
padding: 20px;
}
Übung 3¶
Übungsaufgabe 3 (Grid und Einheiten)
- Erstellen Sie einen
Uebung3-Ordner und darin eine Dateiuebung3.html. Kopieren Sie diesen Inhalt inuebung3.html(Rechtsklick auf die Seite undSeitenquelltext anzeigen- das<script>-Element unten können Sie löschen). - Laden Sie sich hier die Datei images.zip herunter, entpacken Sie sie und schieben Sie den
images-Ordner samt Inhalt in denUebung3-Ordner. - Implementieren Sie die
uebung3.htmlso, dass ungefähr folgendes Aussehen entsteht:
- Sie können die CSS-Eigenschaften innerhalb der
uebung3.htmlim<style>-Element definieren oder wieder in einer externen Datei. - Ziele der Übung sind die Anwendung von CSS-Grid (siehe z.B. hier) sowie die Verwendung von Größen und Einheiten (siehe z.B. hier). Lassen Sie Ihrer Kreativität freien Lauf!
Eine mögliche Lösung für Übung 3
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Uebung 3</title>
<style>
.wrapper {
display: grid;
grid-template-columns: repeat(2, 1fr);
column-gap: 1em;
row-gap: 1em;
}
.citycard {
display: grid;
grid-template-rows: 4fr 1fr;
/* background-color: rgb(251, 237, 220); */
box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
}
.cityimage {
text-align: center;
}
.cityimage img {
margin: 1em;
width: 5em;
}
.citycard:nth-Child(even) .cityname{
text-align: center;
background-color: aliceblue;
}
.citycard:nth-Child(odd) .cityname{
text-align: center;
background-color: rgb(244, 220, 220);
}
.cityname:hover {
background-color: blue;
color: white;
}
@media (min-width: 1600px) {
.wrapper {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
}
</style>
</head>
<body>
<header>
<h2>STÄDTE</h2>
</header>
<main>
<section class="wrapper">
<div class="citycard">
<div class="cityimage">
<img src="./images/berlin.png" alt="Berlin">
</div>
<div class="cityname">
<p>Berlin</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/bernau.png" alt="Bernau">
</div>
<div class="cityname">
<p>Bernau</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/brandenburg.png" alt="Brandenburg">
</div>
<div class="cityname">
<p>Brandenburg</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/bremen.png" alt="Bremen">
</div>
<div class="cityname">
<p>Bremen</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/bremerhaven.png" alt="Bremerhaven">
</div>
<div class="cityname">
<p>Bremerhaven</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/madrid.png" alt="Madrid">
</div>
<div class="cityname">
<p>Madrid</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/magdeburg.png" alt="Magdeburg">
</div>
<div class="cityname">
<p>Magdeburg</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/mainz.png" alt="Mainz">
</div>
<div class="cityname">
<p>Mainz</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/mannheim.png" alt="Mannheim">
</div>
<div class="cityname">
<p>Mannheim</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/Marburg.png" alt="Marburg">
</div>
<div class="cityname">
<p>Marburg</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/newyork.png" alt="New York">
</div>
<div class="cityname">
<p>New York</p>
</div>
</div>
<div class="citycard">
<div class="cityimage">
<img src="./images/stockholm.png" alt="Stockholm">
</div>
<div class="cityname">
<p>Stockholm</p>
</div>
</div>
</section>
</main>
<footer>
</footer>
</body>
</html>
Übung 4¶
Übungsaufgabe 4 (Bootstrap und JavaScript)
- Erstellen Sie einen
Uebung4-Ordner und darin eine Dateiuebung4.html. - Implementieren Sie die
uebung4.htmlmithilfe von Bootstrap so, dass ungefähr folgendes Aussehen entsteht:
- Sind in das Formular Daten eingegeben und wird der
Registrieren-Button gedrückt, dann erscheint mithilfe einer JavaScript-Funktion:
- Wird der
Abbrechen-Button gedrückt, werden alle bereits eingegebenen Daten wieder aus den Textfeldern entfernt. - Ziele der Übung sind die Anwendung von CSS-Bootstrap und JavaScript.
Eine mögliche Lösung für Übung 4
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../css/bootstrap.min.css">
<title>Übung 4</title>
</head>
<body>
<div class="container">
<h1 class="my-3">Teilnehmerin Registrierung</h1>
<div class="row">
<div class="my-3 col-12 col-sm-6 col-md-4 col-lg-3">
<input class="form-control" type="text" placeholder="First name" id="first" />
</div>
<div class="my-3 col-12 col-sm-6 col-md-4 col-lg-3">
<input class="form-control" type="text" placeholder="Last name" id="last" />
</div>
<div class="my-3 col-12 col-sm-6 col-md-4 col-lg-3">
<input class="form-control" type="email" placeholder="E-Mail" id="email" />
</div>
<div class="my-3 col-12 col-sm-6 col-md-4 col-lg-3">
<input class="form-control" type="text" placeholder="IP-Address" id="ipaddr" />
</div>
<div class="my-3 col-12 col-sm-6 col-md-4 col-lg-3">
<input class="btn btn-secondary" type="text" value="Abbrechen"/>
</div>
<div class="my-3 col-12 col-sm-6 col-md-4 col-lg-3">
<input onclick="register()" class="btn btn-success" type="text" value="Registrieren" />
</div>
</div>
<h2 class="my-4">Eingegebene Werte</h2>
<div id="output"></div>
</div>
<script>
function register()
{
// Eingaben auslesen
console.log("Registrieren geklickt");
let firstInput = document.getElementById('first');
let firstValue = firstInput.value;
let lastValue = document.querySelector('#last').value;
let emailValue = document.querySelector('#email').value;
let ipaddrValue = document.querySelector('#ipaddr').value;
// Eingaben auf Konsole ausgeben
console.log('firstValue : ', firstValue)
console.log('lastValue : ', lastValue)
console.log('emailValue : ', emailValue)
console.log('ipaddrValue : ', ipaddrValue)
if(firstValue == "") {
firstInput.classList.remove('is-valid')
firstInput.classList.add('is-invalid')
} else {
firstInput.classList.remove('is-invalid')
firstInput.classList.add('is-valid')
}
let output = document.getElementById('output');
output.innerHTML = `
<ul>
<li>${firstValue}</li>
<li>${lastValue}</li>
<li>${emailValue}</li>
<li>${ipaddrValue}</li>
</ul>`;
}
</script>
</body>
</html>
Übung 5¶
Übungsaufgabe 5 (JavaScript, DOM)
- Laden Sie hier die Datei
Uebung5.zipherunter, entpacken Sie sie und schieben den OrdnerUebung5in Ihren Projektordner. - In der Datei
uebung5.htmlsind einige Dinge vorbereitet:- eine Tabelle mit leerem
<tbody>. Der<tbody>hat dieid='tbody', - wird die Seite geladen, wird die
init()-Funktion aufgerufen (onload='init()'), - eine JavaScript-Funktion
getStaedte(). Diese Funktion "holt" die Dateistaedte.json(liegt imUebung5-Ordner) und gibt sie zurück, - eine Variable
staedtearr, in der das Array geladen werden soll, das diestaedte.jsonenthält. Achtung! das Array selbst ist der Wert, der im JSON unter dem Schlüsselstaedtesteht (schauen Sie sich die Dateistaedte.jsonan), - eine JavaScript-Funktion
createTable(), die Sie verwenden sollen, um die Tabelle mit Werten zu befüllen. Schauen Sie sich auch die Kommentare inuebung5.htmlan.
- eine Tabelle mit leerem
- Befüllen Sie die Tabelle unter Verwendung der Daten aus
staedte.json- für jede neu entstehende Tabellenzeile müssen Sie fünf neue
td-Objekte kreieren und diese an ein neu kreiertestr-Objekt anhängen. Dastr-Obejkt hängen Sie wiederum an dentbody. - die Nummer in der ersten Spalte erstellen Sie einfach fortlaufend mit dem Wert von
nr, den Sie für jede Zeile erhöhen. - der
Info-Button ist ein Hyperlink mit der Bootstrap-Klassebtn; also<a class="btn btn-success btn-sm" href="">Info</a>. Der Wert fürhreffindet sich jeweils unter demlink-Eintrag für jede Stadt instaedte.json. - für das Bild verwenden Sie den
bild-Link ausstaedte.jsonalssrc. Geben Sie auch dem Attributalteinen Wert (diestadtausstaedte.json). - die Tabelle sieht dann so aus:

- für jede neu entstehende Tabellenzeile müssen Sie fünf neue
- Bei Eingabe in das Textfeld von
Filterwird bei jedem Zeichen, das eingegeben wird, die FunktioncreateTable()aufgerufen (sieheoninput="createTable()"). Es sollen nun nur noch die Städte angezeigt werden, deren Stadtnamen oder deren Gründungsjahr zur Eingabe passt.- Wird also z.B.
breingegeben, dann erscheinen nur die Städte, die mitBrbeginnen (Groß- und Kleinschreibung egal, siehetoLowerCase()):
- Wird also z.B.
12eingegeben, dann erscheinen nur die Städte, deren Gründungsjahr mit12beginnt:
- Tipp: Sie laufen in einer Schleife durch das Array, um alle Städte auszulesen. Fügen Sie darin eine Bedingung ein, dass Sie nur die Städte der Tabelle hinzufügen, die der Filter-Eingabe entsprechen.
- Wird also z.B.
Eine mögliche Lösung für Übung 5
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="../css/bootstrap.min.css" rel="stylesheet">
<title>Übung 5</title>
<style>
img {
width: 50px;
}
</style>
</head>
<body class="container" onload="init()">
<h1>Städte</h1>
<div class="row my-4">
<div class="col-2">
<label for="filter">Filter:</label>
</div>
<div class="col-10">
<input type="text" class="form-control" id="filter" oninput="createTable()"/>
</div>
</div>
<table class="table table-striped table-hover align-middle">
<thead>
<tr>
<th scope="col">Nr</th>
<th scope="col">Jahr</th>
<th scope="col">Stadt</th>
<th scope="col">Link</th>
<th scope="col">Bild</th>
</tr>
</thead>
<tbody id="tbody">
</tbody>
</table>
<script>
let staedtearr = [];
async function getStaedte() {
const request = new Request('staedte.json', {
method: 'GET', // GET ist default
headers: {
'Accept': 'application/json'
}
});
const response = await fetch('staedte.json');
console.log(response);
return response.json();
}
function createTable() {
let input = document.getElementById('filter').value;
if(staedtearr.length > 0)
{
let tbody = document.getElementById('tbody');
tbody.innerHTML = '';
let nr = 1;
for(let stadtObj of staedtearr) {
console.log('stadt : ', stadtObj.stadt)
if(input.toLowerCase() == stadtObj.stadt.substring(0,input.length).toLowerCase()) {
let tr = document.createElement('tr');
let td1 = document.createElement('td');
td1.innerText = nr++;
tr.appendChild(td1);
let td2 = document.createElement('td');
td2.innerText = stadtObj.jahr;
tr.appendChild(td2);
let td3 = document.createElement('td');
td3.innerText = stadtObj.stadt;
tr.appendChild(td3);
let td4 = document.createElement('td');
td4.innerHTML = `<a class="btn btn-success btn-sm" href="${stadtObj.link}">Link</a>`;
tr.appendChild(td4);
let td5 = document.createElement('td');
td5.innerHTML = `<img src="${stadtObj.bild}" als="${stadtObj.stadt}" />`;
tr.appendChild(td5);
tbody.appendChild(tr);
}
}
// hier muessen jetzt die einzelnen Zeilen in die Tabelle eingelesen werden
// das Staedte-Array muss ausgelesen werden (z.B. for(stadt of staedtearr)
// Tipp: zunaechst einfach alle anzeigen lassen und erst dann das Filtern einbauen
// Filtern: die Eingabe mit dem jeweiligen Gruendungsjahr bzw. der jeweiligen Stadt
// vergleichen (siehe z.B. substring(0, input.length) und toLowerCase())
// wenn match, dann entsprechende Zeile einfuegen; sonst nicht
}
}
function init() {
getStaedte()
.then( jsonObj => {
console.log( 'body --> ', jsonObj);
return jsonObj.staedte;
})
.then (alleStaedteAlsArray => {
console.log('staedte array -->', alleStaedteAlsArray);
staedtearr = alleStaedteAlsArray;
createTable();
})
// hier getStaedte() aufrufen und
// staedtearr befuellen!
// siehe Promises!
}
</script>
</body>
</html>
Übung 6¶
Übungsaufgabe 6 (Angular - Komponenten)
- Erstellen Sie ein neues Angular-Projekt
Uebung6(siehe hier). - Erstellen Sie mindestens folgende Komponenten:
header,nav,footer,tableundform. -
Gestalten Sie
header,navundfooterso, dass es ungefähr so aussieht:
Diese drei Komponenten sollen mittels Komponentenselektoren in die
AppComponenteingebunden werden. -
Erstellen Sie für die
TableComponentdie Routereadund für dieFormComponentdie Routecreate, so dass fürlocalhost:4200/createungefähr folgende Ansicht erscheint:
Diese Komponente enthält ein Formular. Für
localhost:4200/readerscheint ungefähr folgende Ansicht:
Diese Komponente enthält eine Tabelle. Es ist nur der Tabellenkopf mit den Spaltenüberschriften zu sehen.
-
In der nächsten Übung befüllen wir die Tabelle mithilfe eines Services.
Eine mögliche Lösung für Übung 6
Übung 7¶
Übungsaufgabe 7 (JSON, Direktiven, Bindings)
- Erstellen Sie ein neues Angular-Projekt
Uebung7(siehe hier) oder nutzen Sie Ihre Implementierung ausUebung6. -
Erstellen Sie im
assets-Ordner eine Dateimembers.jsonmit folgendem Inhalt:assets/members.json
[{ "forename": "Catherine", "surname": "Williams", "email": "cwilliamsl@360.cn" }, { "forename": "Adam", "surname": "Anderson", "email": "aanderson8@google.fr" }, { "forename": "Susan", "surname": "Andrews", "email": "sandrewsn@google.co.jp" }, { "forename": "Catherine", "surname": "Andrews", "email": "candrewsp@noaa.gov" }, { "forename": "Alan", "surname": "Bradley", "email": "abradley1c@globo.com" }, { "forename": "Anne", "surname": "Brooks", "email": "abrooks16@bravesites.com" }, { "forename": "Russell", "surname": "Brown", "email": "rbrownq@nifty.com" }, { "forename": "Ryan", "surname": "Burton", "email": "rburton18@foxnews.com" }, { "forename": "Roy", "surname": "Campbell", "email": "rcampbell1@geocities.com" }, { "forename": "Russell", "surname": "Campbell", "email": "rcampbell17@eventbrite.com" }, { "forename": "Bonnie", "surname": "Coleman", "email": "bcoleman11@fc2.com" }, { "forename": "Ernest", "surname": "Coleman", "email": "ecoleman15@businessweek.com" }, { "forename": "Richard", "surname": "Cruz", "email": "rcruz7@unc.edu" }, { "forename": "Sean", "surname": "Cruz", "email": "scruz10@answers.com" }, { "forename": "Rebecca", "surname": "Cunningham", "email": "rcunninghamd@mac.com" }, { "forename": "Margaret", "surname": "Evans", "email": "mevansh@pcworld.com" }, { "forename": "Jeffrey", "surname": "Ford", "email": "jford14@cnet.com" }, { "forename": "Andrea", "surname": "Gardner", "email": "agardnerv@woothemes.com" }, { "forename": "Deborah", "surname": "George", "email": "dgeorge6@furl.net" }, { "forename": "Sean", "surname": "Gibson", "email": "sgibsony@alexa.com" }, { "forename": "Virginia", "surname": "Graham", "email": "vgrahamk@aol.com" }, { "forename": "Steven", "surname": "Hamilton", "email": "shamiltonu@state.tx.us" }, { "forename": "Virginia", "surname": "Hawkins", "email": "vhawkinsf@ehow.com" }, { "forename": "Edward", "surname": "Hicks", "email": "ehicksc@pcworld.com" }, { "forename": "Mark", "surname": "Johnson", "email": "mjohnsonj@hostgator.com" }, { "forename": "Ruth", "surname": "Jordan", "email": "rjordan1a@smugmug.com" }, { "forename": "Antonio", "surname": "Kim", "email": "akim4@odnoklassniki.ru" }, { "forename": "Jennifer", "surname": "Marshall", "email": "jmarshallt@gnu.org" }, { "forename": "Eric", "surname": "Matthews", "email": "ematthews5@independent.co.uk" }, { "forename": "Raymond", "surname": "Mcdonald", "email": "rmcdonald2@ihg.com" }, { "forename": "Eric", "surname": "Miller", "email": "emillere@creativecommons.org" }, { "forename": "Jonathan", "surname": "Morales", "email": "jmoralesa@ovh.net" }, { "forename": "Marie", "surname": "Morgan", "email": "mmorganb@cloudflare.com" }, { "forename": "Amanda", "surname": "Nelson", "email": "anelson13@indiatimes.com" }, { "forename": "Lisa", "surname": "Olson", "email": "lolsonr@telegraph.co.uk" }, { "forename": "Alice", "surname": "Ortiz", "email": "aortizw@histats.com" }, { "forename": "Peter", "surname": "Phillips", "email": "pphillipss@1688.com" }, { "forename": "Matthew", "surname": "Porter", "email": "mporter9@europa.eu" }, { "forename": "Tammy", "surname": "Ray", "email": "trayx@weather.com" }, { "forename": "Mark", "surname": "Richardson", "email": "mrichardson1d@ihg.com" }, { "forename": "Joan", "surname": "Roberts", "email": "jroberts12@alibaba.com" }, { "forename": "Kathleen", "surname": "Rose", "email": "kroseg@pinterest.com" }, { "forename": "Steve", "surname": "Sanders", "email": "ssanders1b@wikispaces.com" }, { "forename": "Shirley", "surname": "Scott", "email": "sscottm@macromedia.com" }, { "forename": "Lillian", "surname": "Stephens", "email": "lstephens19@hugedomains.com" }, { "forename": "Nicole", "surname": "Thompson", "email": "nthompson3@admin.ch" }, { "forename": "Marie", "surname": "Thompson", "email": "mthompsonz@yelp.com" }, { "forename": "Alan", "surname": "Vasquez", "email": "avasquezo@miibeian.gov.cn" }, { "forename": "Mildred", "surname": "Watkins", "email": "mwatkins0@miibeian.gov.cn" }, { "forename": "Eugene", "surname": "Williams", "email": "ewilliamsi@deliciousdays.com" } ] -
Erstellen Sie einen Service
members.service.ts, in dem diemembers.jsonperfetch()eingelesen wird und der eine Funktion zur Verfügung stellt, die allemembersals Array zurückgibt. Erstellen Sie ein passendesMembers-Interface, um die Typsicherheit zu verbessern. -
Befüllen Sie mit den Daten aus
members.jsoneine Tabelle:
-
Implementieren Sie für das Suchfeld die Behandlung des
input-Ereignisses so, dass nur die Teilnehmerinnen in der Tabelle erscheinen, deren Vor- oder Nachnamen den Suchstring enthalten:
-
Alle Bilder sind nur Anregungen, kann gerne ganz anders aussehen. Gerne können Sie auch Bootstrap einbinden und verwenden (siehe hier).
Video zur Übung 7
Eine mögliche Lösung für Übung 7
import { Component, OnInit, inject } from '@angular/core';
import { MembersService } from '../shared/members.service';
import { Member } from '../shared/member';
import { CommonModule } from '@angular/common';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-table',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './table.component.html',
styleUrl: './table.component.css'
})
export class TableComponent implements OnInit {
private service = inject(MembersService);
allmembers: Member[] = [];
filterarray: Member[] = [];
search = new FormControl();
async ngOnInit(): Promise<void> {
this.allmembers = await this.service.getAllMembers();
this.filterarray = this.allmembers;
console.log('in ngOnInit -> allmembers', this.allmembers)
}
filterMembers(){
let searchstring = this.search.value.toLowerCase();
console.log(searchstring);
this.filterarray = this.allmembers.filter( (member) => {
return (member.forename.toLowerCase().includes(searchstring) || member.surname.toLowerCase().includes(searchstring))
});
}
}
<h1>Alle Teilnehmerinnen</h1>
<input type="text" [formControl]="search" (input)="filterMembers()" />
<table>
<thead>
<tr>
<th>Nr</th>
<th>Vorname</th>
<th>Nachname</th>
<th>E-Mail</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of filterarray; index as i;">
<td>{{ i+1 }}</td>
<td>{{ item.forename }}</td>
<td>{{ item.surname }}</td>
<td>{{ item.email }}</td>
</tr>
</tbody>
</table>
import { Injectable } from '@angular/core';
import { Member } from './member';
@Injectable({
providedIn: 'root'
})
export class MembersService {
async getAllMembers(): Promise<Member[]> {
let response = await fetch('../../assets/members.json')
console.log('in service -> response', response)
let members = await response.json();
console.log('in service -> members', members)
return members;
}
}
export interface Member {
forename: string;
surname: string;
email: string;
}
Übung 8¶
Übungsaufgabe 8 (REST-API mit PostgreSQL)
- Erstellen Sie eine REST-API mit folgenden Endpunkten:
- Geben Sie am Anfang Folgendes im Terminal innerhalb Ihres Projekteordners ein (ohne die Kommentare):
- Vewenden Sie PostgreSQL als Datenbankmanagementsystem. Sie können sich entweder eine lokale Instanz installieren oder den HTW-Server Ocean.f4.htw-berlin.de nutzen.
- Implementieren Sie obige CRUD-Funktionalitäten.
- Beachten Sie!: Es soll kein neuer
Userangelegt werden, wenn derusernamebereits verwendet wird und/oder wenn dieemailbereits verwendet wird:- Weder
usernamenochemailexitieren bereits (Response-Body-Datensatz hier von MongoDB erstellt - sieht in Postgres etwas anders aus_id,__v): usernameexistiert bereits:emailexistiert bereits:
- Weder
GET /user/:namesucht nach demusername(Response-Body-Datensatz hier von MongoDB erstellt - sieht in Postgres etwas anders aus_id,__v):- Die
SQL-Queryfür die Erstellung der Tabelleuserskann so aussehen:let query = ' DROP TABLE IF EXISTS users; CREATE TABLE users(id serial PRIMARY KEY, username VARCHAR(50), password VARCHAR(50), email VARCHAR(50), role VARCHAR(50));';
| Endpunkt | Erläuterung |
|---|---|
GET /users | gebe alle user-Einträge zurück |
POST /users | erstelle einen neuen user |
GET /users/:name | gibt den user mit username == name zurück |
DELETE /users/:id | löscht den user mit _id == id |
PUT /users/:id | ändert Daten von user mit _id == id |
| Anweisung | Webseite |
|---|---|
mkdir Uebung8 | Ordner Uebung8erstellen |
npm init | Erstellt das Node.js-Projekt |
npm i express | express.js |
npm i nodemon --save-dev | nodemon |
npm i dotenv | dotenv |
npm i cors | cors |
npm i pg | pg |
npm i pg-format | pg-format |
Eine mögliche Lösung für Übung 8
Video zur Übung 8
Video zur Übung 8 - Teil 2
Übung 9¶
Übungsaufgabe 9 (Angular, Material Design und Formulare)
-
Erstellen Sie ein neues Angular-Projekt
Uebung9. -
Fügen Sie diesem Projekt mithilfe von
ng add @angular/materialMaterial Design hinzu. -
Erzeugen Sie mithilfe von
ng generate @angular/material:navigation naveine neue Komponentenav. Sie dazu Navigation Schematic. -
Die
app.component.htmlsoll nur noch<app-nav />enthalten (alles andere löschen, auch<router-outlet></router-outlet>). Fügen SieNavComponentdemimports-Array inapp.component.tshinzu (RouterOutletkönnen Sie dort löschen). -
Fügen Sie in der
nav.component.htmlunter dem Kommentar<!-- Add Content Here -->den Selektor<router-outlet></router-outlet>ein. Fügen SieRouterOutletdemimports-Array in dernav.component.tshinzu. -
Starten Sie das Projekt mit
ng serve. Je nachdem, welches Farbschema Sie gewählt haben, sieht die Seite nun ungefähr so aus:
Sie können Ihr Farbschema in der
angular.jsonimstyles-Array ändern (siehe hier) -
Erzeugen Sie drei Komponenten
home,tableundformund binden diese über die Routen'',readundcreatedynamisch ein. Suchen Sie in dernav.component.htmlnach den Menüeinträgen und passen Sie das Menü so an, dass die drei Komponenten darüber aufgerufen werden können. -
In der
FormComponenterzeugen wir uns ein Formular. Schauen Sie sich dazu den Abschnitt Reactive Forms unter Angular.dev sowie die Abschnitte Form fields und Input unter Angular Material an.-
Die
FormComponentmussReactiveFormsModuleimimports-Array enthalten! -
Eingabefelder (
input) werden in Angular alsFormControl-Objekte defininiert, z.B.:export class FormComponent { username = new FormControl(''); password = new FormControl(''); email = new FormControl(''); role = new FormControl(''); }FormControlmuss aus@angular/formsimportiert werden. -
Die Verbindung zwischen View (
html) und Controller (ts) wird per[formControl]="username"hergestellt, z.B.:<label for="user-name">username</label> <input id="user-name" type="text" [formControl]="username" /> <label for="pwd">password</label> <input id="pwd" type="password" [formControl]="password" /> <label for="e-mail">email</label> <input id="e-mail" type="email" [formControl]="email" /> <label for="role">role</label> <select id="role" [formControl]="role"> <option value="">--Rolle auswählen--</option> <option value="user">user</option> <option value="admin">admin</option> </select> -
Unter Verwendung von Material Design werden die Elemente
<mat-form-field>,<mat-label>und die EigenschaftmatInputverwendet:<mat-form-field> <mat-label for="user-name">username</mat-label> <input matInput id="user-name" type="text" [formControl]="username" /> </mat-form-field> <br/> <mat-form-field> <mat-label for="pwd">password</mat-label> <input matInput id="pwd" type="password" [formControl]="password" /> </mat-form-field> <br /> <mat-form-field> <mat-label for="e-mail">email</mat-label> <input matInput id="e-mail" type="email" [formControl]="email" /> </mat-form-field> <br /> <mat-form-field> <mat-label for="role">role</mat-label> <select matNativeControl id="role" [formControl]="role"> <option value="">--Rolle auswählen--</option> <option value="user">user</option> <option value="admin">admin</option> </select> </mat-form-field>In der
form.component.tsmuss dazu dasimports-Array entsprechend befüllt werden:imports: [FormsModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule], -
Den einzelnen
input-Elementen können Validatoren hinzugefügt werden, mit denen überprüft werden kann, ob die Eingabe den Anforderungen genügt. Dazu wird die KlasseValidatorsaus@angular/formsverwendet (siehe hier und hier), z.B.:username = new FormControl('', [Validators.required]); password = new FormControl('', [Validators.required, Validators.minLength(8)]); email = new FormControl('', [Validators.required, Validators.email]); role = new FormControl('', [Validators.required]); getErrorMessageUsername(){ if(this.username.hasError('required')) return 'Bitte ausfüllen'; else return ''; } getErrorMessagePassword(){ if(this.password.hasError('required')) return 'Bitte ausfüllen'; else if(this.password.hasError('minlength')) return 'Mindestens 8 Zeichen'; else return ''; } getErrorMessageEmail(){ if(this.email.hasError('required')) return 'Bitte ausfüllen'; else if(this.email.hasError('email')) return 'Keine gültige E-Mail-Adresse'; else return ''; } getErrorMessageRole(){ if(this.role.hasError('required')) return 'Bitte ausfüllen'; else return ''; }Die
getErrorMessage-Funktionen können dann z.B. so im HTML-Code verwendet werden:<mat-form-field class="wide-width"> <mat-label for="user-name">username</mat-label> <input matInput id="user-name" type="text" [formControl]="username" /> @if (username.invalid) { <mat-error>{{getErrorMessageUsername()}}</mat-error> } </mat-form-field> <br/> <mat-form-field class="wide-width"> <mat-label for="pwd">password</mat-label> <input matInput id="pwd" type="password" [formControl]="password" /> @if (password.invalid) { <mat-error>{{getErrorMessagePassword()}}</mat-error> } </mat-form-field> <br /> <mat-form-field class="wide-width"> <mat-label for="e-mail">email</mat-label> <input matInput id="e-mail" type="email" [formControl]="email" /> @if (email.invalid) { <mat-error>{{getErrorMessageEmail()}}</mat-error> } </mat-form-field> <br /> <mat-form-field class="wide-width"> <mat-label for="role">role</mat-label> <select matNativeControl id="role" [formControl]="role" placeholder="role"> <option value="user">user</option> <option value="admin">admin</option> </select> @if (role.invalid) { <mat-error>{{getErrorMessageRole()}}</mat-error> } </mat-form-field> -
Button hinzufügen: Für Matrial Design Buttons siehe hier, z.B.:
<button mat-raised-button [disabled]="formInvalid()" (click)="register()">Registrieren</button>Dazu muss
MatButtonModulein dasimports-Array vonform.component.tseingefügt werden. -
Der Button ist so lange
disabled, so lange das Formular nicht korrekt ausgefüllt ist, d.h.formInvalid()liefert so lange eintruezurück, so lange (mindestens) einer der Fehler auftritt, der eine Error-Message erzeugt (siehe oben). Schreiben Sie die FunktionformInvalid()entsprechend. -
Bei Klick auf den Button (wenn er
enabledist), wird die Funktionregister()aufgerufen. Implementieren Sie diese Funktion so, dass ein Objekt der Art
auf die Konsole ausgegeben wird.
-
In Übung 10 werden wir die so erzeugten Objekte an das Backend senden und in die Datenbank speichern. Außerdem befüllen wir dann die
TableComponentmit Objekten aus der Datenbank.
-
Eine mögliche Lösung für Übung 9
import { Component } from '@angular/core';
import { FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
@Component({
selector: 'app-form',
standalone: true,
imports: [FormsModule, MatButtonModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule],
templateUrl: './form.component.html',
styleUrl: './form.component.css'
})
export class FormComponent {
username = new FormControl('', [Validators.required]);
password = new FormControl('', [Validators.required, Validators.minLength(8)]);
email = new FormControl('', [Validators.required, Validators.email]);
role = new FormControl('', [Validators.required]);
getErrorMessageUsername(){
if(this.username.hasError('required')) return 'Bitte ausfüllen';
else return '';
}
getErrorMessagePassword(){
if(this.password.hasError('required')) return 'Bitte ausfüllen';
else if(this.password.hasError('minlength')) return 'Mindestens 8 Zeichen';
else return '';
}
getErrorMessageEmail(){
if(this.email.hasError('required')) return 'Bitte ausfüllen';
else if(this.email.hasError('email')) return 'Keine gültige E-Mail-Adresse';
else return '';
}
getErrorMessageRole(){
if(this.role.hasError('required')) return 'Bitte ausfüllen';
else return '';
}
formInvalid() {
return this.username.invalid || this.password.invalid || this.email.invalid || this.role.invalid;
}
register() {
if(!this.formInvalid())
{
let user = {
username: this.username.value,
password: this.password.value,
email: this.email.value,
role: this.role.value
}
console.log('user', user)
}
}
}
<div class="form">
<mat-form-field class="wide-width">
<mat-label for="user-name">username</mat-label>
<input matInput id="user-name" type="text" [formControl]="username" />
@if (username.invalid) {
<mat-error>{{getErrorMessageUsername()}}</mat-error>
}
</mat-form-field>
<br />
<mat-form-field class="wide-width">
<mat-label for="pwd">password</mat-label>
<input matInput id="pwd" type="password" [formControl]="password" />
@if (password.invalid) {
<mat-error>{{getErrorMessagePassword()}}</mat-error>
}
</mat-form-field>
<br />
<mat-form-field class="wide-width">
<mat-label for="e-mail">email</mat-label>
<input matInput id="e-mail" type="email" [formControl]="email" />
@if (email.invalid) {
<mat-error>{{getErrorMessageEmail()}}</mat-error>
}
</mat-form-field>
<br />
<mat-form-field class="wide-width">
<mat-label for="role">role</mat-label>
<select matNativeControl id="role" [formControl]="role" placeholder="role">
<option value="user">user</option>
<option value="admin">admin</option>
</select>
@if (role.invalid) {
<mat-error>{{getErrorMessageRole()}}</mat-error>
}
</mat-form-field>
<br/>
<button mat-raised-button [disabled]="formInvalid()" (click)="register()">Registrieren</button>
</div>
.form {
width: 80%;
margin: auto;
margin-top: 5%;
background-color: white;
padding: 2%;
}
.form mat-form-field {
width: 100%;
}
Video zur Übung 9
Übung 10¶
Übungsaufgabe 10 (Frontend-Backend-Anbindung)
-
Starten Sie das Backend aus Übung 8.
-
Nutzen Sie das Frontend aus Übung 9 und erweitern es wie folgt:
-
Binden Sie in die
app.config.tsdas HttpClientModule ein:import { ApplicationConfig, importProvidersFrom } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; import { HttpClientModule } from '@angular/common/http'; export const appConfig: ApplicationConfig = { providers: [provideRouter(routes), importProvidersFrom(HttpClientModule)] }; -
Erstellen Sie sich einen
Backend-Service und einUser-Interface mithilfe der Angular CLI:ng g s shared/backend ng g i shared/user -
Definieren Sie im
User-Interface die Eigenschaftenusername, password, email, role(allesstrings). -
Definieren Sie sich im
Backend-Service Funktionen für die Anbindung der Backend-Endpunkte (GET /users,POST /users,GET /users/:name,DELETE /users/:idundPUT / users/:id). Beachten Sie, dass jeweils ein Observable zurückgegeben wird, z.B.:import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { User } from './user'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class BackendService { backendUrl = 'http://localhost:4000'; constructor(private http: HttpClient) { } getAllUsers(): Observable<User[]> { let endpoint = '/users'; return this.http.get<User[]>(this.backendUrl + endpoint); } deleteOneMember(id: string): Observable<any> { let endpoint = '/users'; return this.http.delete<any>(this.backendUrl + endpoint + "/" + id); } createNewUser(user: User): Observable<User> { let endpoint = '/users'; return this.http.post<User>(this.backendUrl + endpoint, user); } } -
Binden Sie in die
Register-Komponente denBackend-Service ein, z.B.private bs = inject(BackendService);. Lesen Sie alle Werte aus dem Registrierungsformular aus und erzeugen sich damit ein neuesuser-Objekt. Rufen Sie die FunktioncreateNewUser(user)aus demBackend-Service auf, z.B.:this.bs.createNewUser(user).subscribe({ next: (response) => console.log('response', response), error: (err) => console.log(err), complete: () => console.log('register completed') });Prüfen Sie, ob die Daten in die Datenbank gespeichert werden.
-
Binden Sie in die
Table-Komponente denBackend-Service ein und erstellen sich dort einereadAll()-Funktion, in der diegetAllUsers()-Funktion desBackend-Services aufgerufen wird.- Befüllen Sie mit der Response ein
users-Array. - Rufen Sie in der
ngOnInit()-Funktion der Komponente diereadAll()-Funktion auf (die Komponente muss dazuOnInitimplementieren). - Lesen Sie in einer Tabelle in der
table.component.htmlalle User aus demusers-Array aus. - Richten Sie in der Tabelle eine Spalte
Deleteein, deren Einträge aus Buttons bestehen. Für das Klick-Ereignis soll einedelete(id)-Funktion aufgerufen werden, die den Eintrag aus der Datenbank löscht. Rufen Sie in derdelete(id)-Funktion auch diereadAll()-Funktion erneut auf, damit dasusers-Array aktualisiert wird.
- Befüllen Sie mit der Response ein
-