You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/de/7-bank-project/3-data
leestott 7cfaffabb5
🌐 Update translations via Co-op Translator
3 weeks ago
..
README.md 🌐 Update translations via Co-op Translator 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 4 weeks ago

README.md

Erstellen einer Banking-App Teil 3: Methoden zum Abrufen und Verwenden von Daten

Quiz vor der Vorlesung

Quiz vor der Vorlesung

Einführung

Im Kern jeder Webanwendung stehen Daten. Daten können viele Formen annehmen, aber ihr Hauptzweck ist es immer, dem Benutzer Informationen anzuzeigen. Da Webanwendungen immer interaktiver und komplexer werden, ist die Art und Weise, wie der Benutzer auf Informationen zugreift und mit ihnen interagiert, ein zentraler Bestandteil der Webentwicklung.

In dieser Lektion werden wir sehen, wie man Daten asynchron von einem Server abruft und diese Daten verwendet, um Informationen auf einer Webseite anzuzeigen, ohne das HTML neu zu laden.

Voraussetzungen

Für diese Lektion musst du das Login- und Registrierungsformular der Web-App erstellt haben. Außerdem musst du Node.js installieren und die Server-API lokal ausführen, um auf Kontodaten zuzugreifen.

Du kannst testen, ob der Server ordnungsgemäß läuft, indem du diesen Befehl in einem Terminal ausführst:

curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result

AJAX und Datenabruf

Traditionelle Webseiten aktualisieren die angezeigten Inhalte, wenn der Benutzer einen Link auswählt oder Daten über ein Formular sendet, indem die gesamte HTML-Seite neu geladen wird. Jedes Mal, wenn neue Daten geladen werden müssen, liefert der Webserver eine komplett neue HTML-Seite, die vom Browser verarbeitet werden muss. Dies unterbricht die aktuelle Benutzeraktion und schränkt die Interaktionen während des Neuladens ein. Dieser Workflow wird auch als Multi-Page Application oder MPA bezeichnet.

Aktualisierungsworkflow in einer Multi-Page-Anwendung

Als Webanwendungen komplexer und interaktiver wurden, entstand eine neue Technik namens AJAX (Asynchronous JavaScript and XML). Diese Technik ermöglicht es Webanwendungen, Daten asynchron von einem Server zu senden und abzurufen, ohne die HTML-Seite neu laden zu müssen. Das führt zu schnelleren Updates und flüssigeren Benutzerinteraktionen. Wenn neue Daten vom Server empfangen werden, kann die aktuelle HTML-Seite auch mit JavaScript und der DOM-API aktualisiert werden. Im Laufe der Zeit hat sich dieser Ansatz zu dem entwickelt, was heute als Single-Page Application oder SPA bekannt ist.

Aktualisierungsworkflow in einer Single-Page-Anwendung

Als AJAX erstmals eingeführt wurde, war die einzige verfügbare API zum asynchronen Abrufen von Daten XMLHttpRequest. Moderne Browser implementieren jedoch auch die bequemere und leistungsstärkere Fetch API, die Promises verwendet und besser für die Verarbeitung von JSON-Daten geeignet ist.

Obwohl alle modernen Browser die Fetch API unterstützen, solltest du, wenn deine Webanwendung auch auf älteren Browsern funktionieren soll, immer zuerst die Kompatibilitätstabelle auf caniuse.com überprüfen.

Aufgabe

In der vorherigen Lektion haben wir das Registrierungsformular implementiert, um ein Konto zu erstellen. Jetzt fügen wir Code hinzu, um sich mit einem bestehenden Konto anzumelden und dessen Daten abzurufen. Öffne die Datei app.js und füge eine neue login-Funktion hinzu:

async function login() {
  const loginForm = document.getElementById('loginForm')
  const user = loginForm.user.value;
}

Hier beginnen wir damit, das Formularelement mit getElementById() abzurufen, und holen dann den Benutzernamen aus dem Eingabefeld mit loginForm.user.value. Jedes Formularelement kann über seinen Namen (im HTML mit dem Attribut name festgelegt) als Eigenschaft des Formulars aufgerufen werden.

Ähnlich wie bei der Registrierung erstellen wir eine weitere Funktion, um eine Serveranfrage auszuführen, diesmal jedoch, um die Kontodaten abzurufen:

async function getAccount(user) {
  try {
    const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user));
    return await response.json();
  } catch (error) {
    return { error: error.message || 'Unknown error' };
  }
}

Wir verwenden die fetch API, um die Daten asynchron vom Server anzufordern. Diesmal benötigen wir keine zusätzlichen Parameter außer der URL, da wir nur Daten abfragen. Standardmäßig erstellt fetch eine GET-HTTP-Anfrage, was genau das ist, was wir hier benötigen.

encodeURIComponent() ist eine Funktion, die Sonderzeichen für URLs maskiert. Welche Probleme könnten auftreten, wenn wir diese Funktion nicht aufrufen und den user-Wert direkt in der URL verwenden?

Nun aktualisieren wir unsere login-Funktion, um getAccount zu verwenden:

async function login() {
  const loginForm = document.getElementById('loginForm')
  const user = loginForm.user.value;
  const data = await getAccount(user);

  if (data.error) {
    return console.log('loginError', data.error);
  }

  account = data;
  navigate('/dashboard');
}

Da getAccount eine asynchrone Funktion ist, müssen wir sie mit dem Schlüsselwort await verwenden, um auf das Serverergebnis zu warten. Wie bei jeder Serveranfrage müssen wir auch Fehlerfälle behandeln. Vorerst fügen wir nur eine Log-Nachricht hinzu, um den Fehler anzuzeigen, und kommen später darauf zurück.

Dann müssen wir die Daten irgendwo speichern, damit wir sie später verwenden können, um die Dashboard-Informationen anzuzeigen. Da die Variable account noch nicht existiert, erstellen wir eine globale Variable dafür am Anfang unserer Datei:

let account = null;

Nachdem die Benutzerdaten in einer Variablen gespeichert wurden, können wir mit der Funktion navigate() von der Login-Seite zum Dashboard wechseln.

Schließlich müssen wir unsere login-Funktion aufrufen, wenn das Login-Formular abgeschickt wird, indem wir das HTML ändern:

<form id="loginForm" action="javascript:login()">

Teste, ob alles korrekt funktioniert, indem du ein neues Konto registrierst und versuchst, dich mit demselben Konto anzumelden.

Bevor wir zum nächsten Teil übergehen, können wir auch die register-Funktion vervollständigen, indem wir dies am Ende der Funktion hinzufügen:

account = result;
navigate('/dashboard');

Wusstest du, dass du standardmäßig nur Server-APIs von derselben Domain und Port wie die Webseite, die du ansiehst, aufrufen kannst? Dies ist ein Sicherheitsmechanismus, der von Browsern durchgesetzt wird. Aber Moment mal, unsere Web-App läuft auf localhost:3000, während die Server-API auf localhost:5000 läuft. Warum funktioniert es trotzdem? Durch die Verwendung einer Technik namens Cross-Origin Resource Sharing (CORS) ist es möglich, Cross-Origin-HTTP-Anfragen auszuführen, wenn der Server spezielle Header zur Antwort hinzufügt, die Ausnahmen für bestimmte Domains erlauben.

Erfahre mehr über APIs, indem du diese Lektion durcharbeitest.

HTML aktualisieren, um Daten anzuzeigen

Jetzt, da wir die Benutzerdaten haben, müssen wir das bestehende HTML aktualisieren, um sie anzuzeigen. Wir wissen bereits, wie man ein Element aus dem DOM abruft, z. B. mit document.getElementById(). Nachdem du ein Basiselement hast, kannst du mit diesen APIs den Inhalt ändern oder Kind-Elemente hinzufügen:

  • Mit der textContent-Eigenschaft kannst du den Text eines Elements ändern. Beachte, dass das Ändern dieses Werts alle Kind-Elemente des Elements (falls vorhanden) entfernt und durch den angegebenen Text ersetzt. Daher ist es auch eine effiziente Methode, um alle Kinder eines bestimmten Elements zu entfernen, indem man ihm einen leeren String '' zuweist.

  • Mit document.createElement() zusammen mit der append()-Methode kannst du ein oder mehrere neue Kind-Elemente erstellen und anhängen.

Mit der innerHTML-Eigenschaft eines Elements ist es ebenfalls möglich, dessen HTML-Inhalt zu ändern. Diese Methode sollte jedoch vermieden werden, da sie anfällig für Cross-Site-Scripting (XSS)-Angriffe ist.

Aufgabe

Bevor wir zur Dashboard-Seite übergehen, gibt es noch etwas, das wir auf der Login-Seite tun sollten. Derzeit wird, wenn du versuchst, dich mit einem nicht existierenden Benutzernamen anzumelden, eine Nachricht in der Konsole angezeigt, aber für einen normalen Benutzer ändert sich nichts, und man weiß nicht, was los ist.

Fügen wir ein Platzhalter-Element im Login-Formular hinzu, in dem wir bei Bedarf eine Fehlermeldung anzeigen können. Ein guter Platz wäre direkt vor dem Login-<button>:

...
<div id="loginError"></div>
<button>Login</button>
...

Dieses <div>-Element ist leer, was bedeutet, dass nichts auf dem Bildschirm angezeigt wird, bis wir ihm Inhalte hinzufügen. Wir geben ihm auch eine id, damit wir es leicht mit JavaScript abrufen können.

Gehe zurück zur Datei app.js und erstelle eine neue Hilfsfunktion updateElement:

function updateElement(id, text) {
  const element = document.getElementById(id);
  element.textContent = text;
}

Diese Funktion ist ziemlich einfach: Sie aktualisiert den Textinhalt des DOM-Elements mit der angegebenen id und dem übergebenen Text. Lass uns diese Methode anstelle der vorherigen Fehlermeldung in der login-Funktion verwenden:

if (data.error) {
  return updateElement('loginError', data.error);
}

Wenn du jetzt versuchst, dich mit einem ungültigen Konto anzumelden, solltest du etwas wie das Folgende sehen:

Screenshot, der die Fehlermeldung beim Login anzeigt

Jetzt haben wir einen Fehlertext, der visuell angezeigt wird. Wenn du jedoch einen Screenreader verwendest, wirst du feststellen, dass nichts angekündigt wird. Damit Text, der dynamisch zu einer Seite hinzugefügt wird, von Screenreadern angekündigt wird, muss er eine sogenannte Live Region verwenden. Hier verwenden wir eine spezielle Art von Live-Region, die als Alert bezeichnet wird:

<div id="loginError" role="alert"></div>

Implementiere das gleiche Verhalten für die Fehler in der register-Funktion (vergiss nicht, das HTML zu aktualisieren).

Informationen im Dashboard anzeigen

Mit den gleichen Techniken, die wir gerade gesehen haben, kümmern wir uns auch um die Anzeige der Kontoinformationen auf der Dashboard-Seite.

So sieht ein vom Server empfangenes Kontoobjekt aus:

{
  "user": "test",
  "currency": "$",
  "description": "Test account",
  "balance": 75,
  "transactions": [
    { "id": "1", "date": "2020-10-01", "object": "Pocket money", "amount": 50 },
    { "id": "2", "date": "2020-10-03", "object": "Book", "amount": -10 },
    { "id": "3", "date": "2020-10-04", "object": "Sandwich", "amount": -5 }
  ],
}

Hinweis: Um dir das Leben zu erleichtern, kannst du das vorab erstellte test-Konto verwenden, das bereits mit Daten gefüllt ist.

Aufgabe

Beginnen wir damit, den Abschnitt "Balance" im HTML zu ersetzen, um Platzhalter-Elemente hinzuzufügen:

<section>
  Balance: <span id="balance"></span><span id="currency"></span>
</section>

Wir fügen auch einen neuen Abschnitt direkt darunter hinzu, um die Kontobeschreibung anzuzeigen:

<h2 id="description"></h2>

Da die Kontobeschreibung als Titel für den darunter liegenden Inhalt fungiert, wird sie semantisch als Überschrift ausgezeichnet. Erfahre mehr darüber, wie Überschriftenstruktur für die Barrierefreiheit wichtig ist, und wirf einen kritischen Blick auf die Seite, um zu bestimmen, was sonst noch eine Überschrift sein könnte.

Als Nächstes erstellen wir eine neue Funktion in app.js, um die Platzhalter auszufüllen:

function updateDashboard() {
  if (!account) {
    return navigate('/login');
  }

  updateElement('description', account.description);
  updateElement('balance', account.balance.toFixed(2));
  updateElement('currency', account.currency);
}

Zuerst überprüfen wir, ob wir die benötigten Kontodaten haben, bevor wir fortfahren. Dann verwenden wir die Funktion updateElement(), die wir zuvor erstellt haben, um das HTML zu aktualisieren.

Um die Anzeige des Kontostands ansprechender zu gestalten, verwenden wir die Methode toFixed(2), um den Wert mit 2 Nachkommastellen anzuzeigen.

Nun müssen wir unsere updateDashboard()-Funktion jedes Mal aufrufen, wenn das Dashboard geladen wird. Wenn du die Aufgabe der Lektion 1 bereits abgeschlossen hast, sollte dies einfach sein. Andernfalls kannst du die folgende Implementierung verwenden.

Füge diesen Code am Ende der Funktion updateRoute() hinzu:

if (typeof route.init === 'function') {
  route.init();
}

Und aktualisiere die Routen-Definition mit:

const routes = {
  '/login': { templateId: 'login' },
  '/dashboard': { templateId: 'dashboard', init: updateDashboard }
};

Mit dieser Änderung wird die Funktion updateDashboard() jedes Mal aufgerufen, wenn die Dashboard-Seite angezeigt wird. Nach einem Login solltest du dann den Kontostand, die Währung und die Beschreibung sehen können.

Tabellenzeilen dynamisch mit HTML-Templates erstellen

In der ersten Lektion haben wir HTML-Templates zusammen mit der Methode appendChild() verwendet, um die Navigation in unserer App zu implementieren. Templates können auch kleiner sein und verwendet werden, um wiederholte Teile einer Seite dynamisch zu füllen.

Wir verwenden einen ähnlichen Ansatz, um die Liste der Transaktionen in der HTML-Tabelle anzuzeigen.

Aufgabe

Füge ein neues Template in den HTML-<body>-Bereich ein:

<template id="transaction">
  <tr>
    <td></td>
    <td></td>
    <td></td>
  </tr>
</template>

Dieses Template stellt eine einzelne Tabellenzeile dar, mit den 3 Spalten, die wir ausfüllen möchten: Datum, Objekt und Betrag einer Transaktion.

Füge dann diese id-Eigenschaft zum <tbody>-Element der Tabelle innerhalb des Dashboard-Templates hinzu, um es mit JavaScript leichter zu finden:

<tbody id="transactions"></tbody>

Unser HTML ist bereit. Wechseln wir zum JavaScript-Code und erstellen eine neue Funktion createTransactionRow:

function createTransactionRow(transaction) {
  const template = document.getElementById('transaction');
  const transactionRow = template.content.cloneNode(true);
  const tr = transactionRow.querySelector('tr');
  tr.children[0].textContent = transaction.date;
  tr.children[1].textContent = transaction.object;
  tr.children[2].textContent = transaction.amount.toFixed(2);
  return transactionRow;
}

Diese Funktion macht genau das, was ihr Name andeutet: Sie verwendet das zuvor erstellte Template, um eine neue Tabellenzeile zu erstellen und deren Inhalte mit Transaktionsdaten zu füllen. Wir verwenden dies in unserer updateDashboard()-Funktion, um die Tabelle zu füllen:

const transactionsRows = document.createDocumentFragment();
for (const transaction of account.transactions) {
  const transactionRow = createTransactionRow(transaction);
  transactionsRows.appendChild(transactionRow);
}
updateElement('transactions', transactionsRows);

Hier verwenden wir die Methode document.createDocumentFragment(), die ein neues DOM-Fragment erstellt, an dem wir arbeiten können, bevor wir es schließlich an unsere HTML-Tabelle anhängen.

Es gibt noch eine Sache, die wir tun müssen, bevor dieser Code funktioniert, da unsere Funktion updateElement() derzeit nur Textinhalte unterstützt. Ändern wir ihren Code ein wenig:

function updateElement(id, textOrNode) {
  const element = document.getElementById(id);
  element.textContent = ''; // Removes all children
  element.append(textOrNode);
}

Wir verwenden die Methode append(), da sie es ermöglicht, entweder Text oder DOM-Knoten an ein übergeordnetes Element anzuhängen, was perfekt für all unsere Anwendungsfälle ist. Wenn du versuchst, dich mit dem test-Konto anzumelden, solltest du jetzt eine Transaktionsliste auf dem Dashboard sehen 🎉.


🚀 Herausforderung

Arbeitet zusammen, um die Dashboard-Seite wie eine echte Banking-App aussehen zu lassen. Falls ihr eure App bereits gestaltet habt, versucht Media Queries zu verwenden, um ein responsives Design zu erstellen, das sowohl auf Desktop- als auch auf Mobilgeräten gut funktioniert.

Hier ist ein Beispiel für eine gestaltete Dashboard-Seite:

Screenshot eines Beispielergebnisses des Dashboards nach der Gestaltung

Quiz nach der Vorlesung

Quiz nach der Vorlesung

Aufgabe

Refaktoriert und kommentiert euren Code


Haftungsausschluss:
Dieses Dokument wurde mit dem KI-Übersetzungsdienst Co-op Translator übersetzt. Obwohl wir uns um Genauigkeit bemühen, weisen wir darauf hin, dass automatisierte Übersetzungen Fehler oder Ungenauigkeiten enthalten können. Das Originaldokument in seiner ursprünglichen Sprache sollte als maßgebliche Quelle betrachtet werden. Für kritische Informationen wird eine professionelle menschliche Übersetzung empfohlen. Wir übernehmen keine Haftung für Missverständnisse oder Fehlinterpretationen, die sich aus der Nutzung dieser Übersetzung ergeben.