# Byg en Bankapp Del 3: Metoder til Hentning og Brug af Data ## Quiz før forelæsning [Quiz før forelæsning](https://ff-quizzes.netlify.app/web/quiz/45) ### Introduktion Kernen i enhver webapplikation er *data*. Data kan have mange former, men dens hovedformål er altid at vise information til brugeren. Med webapps, der bliver stadig mere interaktive og komplekse, er måden, brugeren tilgår og interagerer med information på, nu en vigtig del af webudvikling. I denne lektion vil vi se, hvordan man henter data fra en server asynkront og bruger disse data til at vise information på en webside uden at genindlæse HTML'en. ### Forudsætninger Du skal have bygget [Login- og Registreringsformularen](../2-forms/README.md) som en del af webappen for denne lektion. Du skal også installere [Node.js](https://nodejs.org) og [køre server-API'et](../api/README.md) lokalt, så du kan få kontodata. Du kan teste, om serveren kører korrekt, ved at udføre denne kommando i en terminal: ```sh curl http://localhost:5000/api # -> should return "Bank API v1.0.0" as a result ``` --- ## AJAX og datahentning Traditionelle websites opdaterer det viste indhold, når brugeren vælger et link eller sender data via en formular, ved at genindlæse hele HTML-siden. Hver gang nye data skal indlæses, returnerer webserveren en helt ny HTML-side, som skal behandles af browseren, hvilket afbryder den aktuelle brugerhandling og begrænser interaktioner under genindlæsningen. Denne arbejdsgang kaldes også en *Multi-Page Application* eller *MPA*.  Da webapplikationer begyndte at blive mere komplekse og interaktive, opstod en ny teknik kaldet [AJAX (Asynchronous JavaScript and XML)](https://en.wikipedia.org/wiki/Ajax_(programming)). Denne teknik gør det muligt for webapps at sende og hente data fra en server asynkront ved hjælp af JavaScript, uden at skulle genindlæse HTML-siden, hvilket resulterer i hurtigere opdateringer og mere glidende brugerinteraktioner. Når nye data modtages fra serveren, kan den aktuelle HTML-side også opdateres med JavaScript ved hjælp af [DOM](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)-API'en. Over tid har denne tilgang udviklet sig til det, der nu kaldes en [*Single-Page Application* eller *SPA*](https://en.wikipedia.org/wiki/Single-page_application).  Da AJAX først blev introduceret, var den eneste API tilgængelig for asynkron datahentning [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest). Men moderne browsere implementerer nu også den mere praktiske og kraftfulde [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API), som bruger promises og er bedre egnet til at manipulere JSON-data. > Selvom alle moderne browsere understøtter `Fetch API`, er det altid en god idé at tjekke [kompatibilitetstabellen på caniuse.com](https://caniuse.com/fetch), hvis du vil have din webapplikation til at fungere på ældre browsere. ### Opgave I [den forrige lektion](../2-forms/README.md) implementerede vi registreringsformularen for at oprette en konto. Nu vil vi tilføje kode til at logge ind med en eksisterende konto og hente dens data. Åbn filen `app.js` og tilføj en ny `login`-funktion: ```js async function login() { const loginForm = document.getElementById('loginForm') const user = loginForm.user.value; } ``` Her starter vi med at hente formular-elementet med `getElementById()`, og derefter får vi brugernavnet fra inputfeltet med `loginForm.user.value`. Hvert formularfelt kan tilgås via dets navn (sat i HTML med attributten `name`) som en egenskab af formularen. På samme måde som vi gjorde for registreringen, vil vi oprette en anden funktion til at udføre en serverforespørgsel, men denne gang for at hente kontodata: ```js 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' }; } } ``` Vi bruger `fetch`-API'et til at anmode om data asynkront fra serveren, men denne gang behøver vi ikke andre parametre end URL'en, da vi kun forespørger data. Som standard opretter `fetch` en [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET) HTTP-forespørgsel, hvilket er det, vi søger her. ✅ `encodeURIComponent()` er en funktion, der undgår specialtegn i en URL. Hvilke problemer kunne vi muligvis få, hvis vi ikke kalder denne funktion og bruger `user`-værdien direkte i URL'en? Lad os nu opdatere vores `login`-funktion til at bruge `getAccount`: ```js 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'); } ``` Først, da `getAccount` er en asynkron funktion, skal vi matche den med nøgleordet `await` for at vente på serverresultatet. Som med enhver serverforespørgsel skal vi også håndtere fejltilfælde. For nu vil vi kun tilføje en logbesked for at vise fejlen og vende tilbage til det senere. Derefter skal vi gemme dataene et sted, så vi senere kan bruge dem til at vise dashboard-information. Da variablen `account` endnu ikke eksisterer, opretter vi en global variabel til den øverst i vores fil: ```js let account = null; ``` Efter brugerdataene er gemt i en variabel, kan vi navigere fra *login*-siden til *dashboard*-siden ved hjælp af funktionen `navigate()`, som vi allerede har. Endelig skal vi kalde vores `login`-funktion, når loginformularen sendes, ved at ændre HTML'en: ```html