# Byg en bankapp del 3: Metoder til at hente og bruge data Tænk på Enterprise's computer i Star Trek - når kaptajn Picard spørger om skibets status, vises informationen øjeblikkeligt uden at hele grænsefladen lukker ned og genopbygges. Den sømløse informationsstrøm er præcis, hvad vi bygger her med dynamisk datahentning. Lige nu er din bankapp som en trykt avis - informativ, men statisk. Vi vil transformere den til noget mere som mission control hos NASA, hvor data flyder kontinuerligt og opdateres i realtid uden at forstyrre brugerens arbejdsflow. Du vil lære, hvordan man kommunikerer med servere asynkront, håndterer data, der ankommer på forskellige tidspunkter, og omdanner rå information til noget meningsfuldt for dine brugere. Dette er forskellen mellem en demo og software, der er klar til produktion. ## Quiz før lektionen [Quiz før lektionen](https://ff-quizzes.netlify.app/web/quiz/45) ### Forudsætninger Før du dykker ned i datahentning, skal du sikre dig, at du har disse komponenter klar: - **Forrige lektion**: Fuldfør [Login- og registreringsformularen](../2-forms/README.md) - vi bygger videre på dette fundament - **Lokal server**: Installer [Node.js](https://nodejs.org) og [kør server-API'en](../api/README.md) for at levere kontodata - **API-forbindelse**: Test din serverforbindelse med denne kommando: ```bash curl http://localhost:5000/api # Expected response: "Bank API v1.0.0" ``` Denne hurtige test sikrer, at alle komponenter kommunikerer korrekt: - Bekræfter, at Node.js kører korrekt på dit system - Bekræfter, at din API-server er aktiv og reagerer - Validerer, at din app kan nå serveren (som at tjekke radiokontakt før en mission) --- ## Forstå datahentning i moderne webapps Måden, webapplikationer håndterer data på, har udviklet sig dramatisk over de sidste to årtier. At forstå denne udvikling vil hjælpe dig med at værdsætte, hvorfor moderne teknikker som AJAX og Fetch API er så kraftfulde og hvorfor de er blevet essentielle værktøjer for webudviklere. Lad os udforske, hvordan traditionelle websites fungerede sammenlignet med de dynamiske, responsive applikationer, vi bygger i dag. ### Traditionelle multi-side applikationer (MPA) I internettets tidlige dage var hvert klik som at skifte kanal på et gammelt fjernsyn - skærmen blev sort, og derefter tunede den langsomt ind på det nye indhold. Dette var virkeligheden for tidlige webapplikationer, hvor hver interaktion betød, at hele siden blev genopbygget fra bunden. ```mermaid sequenceDiagram participant User participant Browser participant Server User->>Browser: Clicks link or submits form Browser->>Server: Requests new HTML page Note over Browser: Page goes blank Server->>Browser: Returns complete HTML page Browser->>User: Displays new page (flash/reload) ```  **Hvorfor denne tilgang føltes klodset:** - Hvert klik betød, at hele siden blev genopbygget fra bunden - Brugere blev afbrudt midt i deres tanker af de irriterende sideblink - Din internetforbindelse arbejdede overtid med at downloade den samme header og footer igen og igen - Apps føltes mere som at klikke gennem et arkivskab end at bruge software ### Moderne single-side applikationer (SPA) AJAX (Asynchronous JavaScript and XML) ændrede denne paradigme fuldstændigt. Ligesom det modulære design af den internationale rumstation, hvor astronauter kan udskifte individuelle komponenter uden at genopbygge hele strukturen, giver AJAX os mulighed for at opdatere specifikke dele af en webside uden at genindlæse alt. Selvom navnet nævner XML, bruger vi mest JSON i dag, men grundprincippet forbliver: opdater kun det, der skal ændres. ```mermaid sequenceDiagram participant User participant Browser participant JavaScript participant Server User->>Browser: Interacts with page Browser->>JavaScript: Triggers event handler JavaScript->>Server: Fetches only needed data Server->>JavaScript: Returns JSON data JavaScript->>Browser: Updates specific page elements Browser->>User: Shows updated content (no reload) ```  **Hvorfor SPAs føles så meget bedre:** - Kun de dele, der faktisk ændres, bliver opdateret (smart, ikke?) - Ingen flere forstyrrende afbrydelser - dine brugere forbliver i deres flow - Mindre data, der rejser over nettet, betyder hurtigere indlæsning - Alt føles hurtigt og responsivt, som apps på din telefon ### Udviklingen til moderne Fetch API Moderne browsere tilbyder [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API), som erstatter den ældre [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest). Ligesom forskellen mellem at betjene en telegraf og bruge e-mail, bruger Fetch API promises for renere asynkron kode og håndterer JSON naturligt. | Funktion | XMLHttpRequest | Fetch API | |----------|----------------|-----------| | **Syntaks** | Kompliceret callback-baseret | Ren promise-baseret | | **JSON-håndtering** | Kræver manuel parsing | Indbygget `.json()`-metode | | **Fejlhåndtering** | Begrænset fejlinformation | Omfattende fejldetaljer | | **Moderne support** | Legacy-kompatibilitet | ES6+ promises og async/await | > 💡 **Browserkompatibilitet**: Gode nyheder - Fetch API fungerer i alle moderne browsere! Hvis du er nysgerrig på specifikke versioner, har [caniuse.com](https://caniuse.com/fetch) den komplette kompatibilitetshistorik. > **Konklusionen:** - Fungerer godt i Chrome, Firefox, Safari og Edge (grundlæggende overalt, hvor dine brugere er) - Kun Internet Explorer har brug for ekstra hjælp (og ærligt talt, det er tid til at sige farvel til IE) - Forbereder dig perfekt til de elegante async/await-mønstre, vi vil bruge senere ### Implementering af brugerlogin og datahentning Lad os nu implementere login-systemet, der forvandler din bankapp fra en statisk visning til en funktionel applikation. Ligesom autentifikationsprotokoller, der bruges i sikre militæranlæg, vil vi verificere brugerens legitimationsoplysninger og derefter give adgang til deres specifikke data. Vi bygger dette trin for trin, startende med grundlæggende autentifikation og derefter tilføje datahentningsfunktioner. #### Trin 1: Opret fundamentet for login-funktionen Åbn din `app.js`-fil og tilføj en ny `login`-funktion. Denne vil håndtere brugerens autentifikationsproces: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **Lad os bryde dette ned:** - Det `async` nøgleord? Det fortæller JavaScript "hey, denne funktion skal måske vente på ting" - Vi henter vores formular fra siden (ikke noget fancy, bare finder den via dens ID) - Derefter trækker vi det ud, som brugeren har indtastet som deres brugernavn - Her er et smart trick: du kan få adgang til enhver formularinput via dens `name`-attribut - ingen grund til ekstra getElementById-opkald! > 💡 **Formularadgangsmønster**: Hver formularkontrol kan tilgås via dens navn (angivet i HTML ved hjælp af `name`-attributten) som en egenskab af form-elementet. Dette giver en ren, læsbar måde at få formulardata på. #### Trin 2: Opret funktionen til at hente kontodata Dernæst opretter vi en dedikeret funktion til at hente kontodata fra serveren. Dette følger samme mønster som din registreringsfunktion, men fokuserer på datahentning: ```javascript 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' }; } } ``` **Her er, hvad denne kode opnår:** - **Bruger** det moderne `fetch` API til at anmode om data asynkront - **Konstruerer** en GET-anmodnings-URL med brugernavnparameteren - **Anvender** `encodeURIComponent()` for sikkert at håndtere specialtegn i URL'er - **Konverterer** svaret til JSON-format for nem datamanipulation - **Håndterer** fejl elegant ved at returnere et fejlobjekt i stedet for at gå ned > ⚠️ **Sikkerhedsnotat**: Funktionen `encodeURIComponent()` håndterer specialtegn i URL'er. Ligesom kodningssystemer, der bruges i flådekommunikation, sikrer den, at din besked ankommer præcis som tilsigtet, og forhindrer tegn som "#" eller "&" i at blive misfortolket. > **Hvorfor dette er vigtigt:** - Forhindrer specialtegn i at ødelægge URL'er - Beskytter mod URL-manipulationsangreb - Sikrer, at din server modtager de tilsigtede data - Følger sikre kodningspraksisser #### Forstå HTTP GET-anmodninger Her er noget, der måske overrasker dig: når du bruger `fetch` uden ekstra muligheder, opretter den automatisk en [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET)-anmodning. Dette er perfekt til det, vi gør - spørger serveren "hey, kan jeg se denne brugers kontodata?" Tænk på GET-anmodninger som høfligt at bede om at låne en bog fra biblioteket - du anmoder om at se noget, der allerede eksisterer. POST-anmodninger (som vi brugte til registrering) er mere som at indsende en ny bog, der skal tilføjes til samlingen. | GET-anmodning | POST-anmodning | |---------------|----------------| | **Formål** | Hente eksisterende data | Sende nye data til serveren | | **Parametre** | I URL-sti/spørgsmålstegn | I anmodningskroppen | | **Caching** | Kan caches af browsere | Typisk ikke cached | | **Sikkerhed** | Synlig i URL/logs | Skjult i anmodningskroppen | #### Trin 3: Samle det hele Nu til den tilfredsstillende del - lad os forbinde din kontohentningsfunktion til login-processen. Dette er, hvor alt falder på plads: ```javascript 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'); } ``` Denne funktion følger en klar sekvens: - Udtrækker brugernavnet fra formularinputtet - Anmoder om brugerens kontodata fra serveren - Håndterer eventuelle fejl, der opstår under processen - Gemmer kontodataene og navigerer til dashboardet ved succes > 🎯 **Async/Await-mønster**: Da `getAccount` er en asynkron funktion, bruger vi nøgleordet `await` for at pause udførelsen, indtil serveren svarer. Dette forhindrer koden i at fortsætte med udefinerede data. #### Trin 4: Opret et hjem for dine data Din app har brug for et sted at huske kontoinformationen, når den er indlæst. Tænk på dette som appens korttidshukommelse - et sted at holde den aktuelle brugers data lige ved hånden. Tilføj denne linje øverst i din `app.js`-fil: ```javascript // This holds the current user's account data let account = null; ``` **Hvorfor vi har brug for dette:** - Holder kontodataene tilgængelige fra hvor som helst i din app - Start med `null` betyder "ingen er logget ind endnu" - Bliver opdateret, når nogen logger ind eller registrerer sig med succes - Fungerer som en enkelt sandhedskilde - ingen forvirring om, hvem der er logget ind #### Trin 5: Tilslut din formular Lad os nu forbinde din nye login-funktion til din HTML-formular. Opdater din formular-tag sådan her: ```html
``` **Hvad denne lille ændring gør:** - Stopper formularen fra at udføre sin standard "genindlæs hele siden"-adfærd - Kalder din brugerdefinerede JavaScript-funktion i stedet - Holder alt glat og single-page-app-lignende - Giver dig fuld kontrol over, hvad der sker, når brugerne trykker på "Login" #### Trin 6: Forbedr din registreringsfunktion For konsistens, opdater din `register`-funktion til også at gemme kontodata og navigere til dashboardet: ```javascript // Add these lines at the end of your register function account = result; navigate('/dashboard'); ``` **Denne forbedring giver:** - **Sømløs** overgang fra registrering til dashboard - **Konsistent** brugeroplevelse mellem login- og registreringsforløb - **Øjeblikkelig** adgang til kontodata efter vellykket registrering #### Test din implementering ```mermaid flowchart TD A[User enters credentials] --> B[Login function called] B --> C[Fetch account data from server] C --> D{Data received successfully?} D -->|Yes| E[Store account data globally] D -->|No| F[Display error message] E --> G[Navigate to dashboard] F --> H[User stays on login page] ``` **Tid til at prøve det af:** 1. Opret en ny konto for at sikre, at alt fungerer 2. Prøv at logge ind med de samme legitimationsoplysninger 3. Kig på din browsers konsol (F12), hvis noget virker forkert 4. Sørg for, at du lander på dashboardet efter en vellykket login Hvis noget ikke fungerer, så gå ikke i panik! De fleste problemer er simple rettelser som tastefejl eller at glemme at starte API-serveren. #### Et hurtigt ord om Cross-Origin magi Du undrer dig måske: "Hvordan taler min webapp med denne API-server, når de kører på forskellige porte?" Godt spørgsmål! Dette berører noget, som enhver webudvikler støder på før eller siden. > 🔒 **Cross-Origin sikkerhed**: Browsere håndhæver en "same-origin policy" for at forhindre uautoriseret kommunikation mellem forskellige domæner. Ligesom kontrolsystemet ved Pentagon verificerer de, at kommunikationen er autoriseret, før de tillader dataoverførsel. > **I vores opsætning:** - Din webapp kører på `localhost:3000` (udviklingsserver) - Din API-server kører på `localhost:5000` (backend-server) - API-serveren inkluderer [CORS headers](https://developer.mozilla.org/docs/Web/HTTP/CORS), der eksplicit autoriserer kommunikation fra din webapp Denne konfiguration afspejler virkelighedens udvikling, hvor frontend- og backend-applikationer typisk kører på separate servere. > 📚 **Lær mere**: Dyk dybere ned i API'er og datahentning med dette omfattende [Microsoft Learn modul om API'er](https://docs.microsoft.com/learn/modules/use-apis-discover-museum-art/?WT.mc_id=academic-77807-sagibbon). ## Bring dine data til live i HTML Nu vil vi gøre de hentede data synlige for brugerne gennem DOM-manipulation. Ligesom processen med at fremkalde fotografier i et mørkekammer, tager vi usynlige data og gør dem til noget, brugerne kan se og interagere med. DOM-manipulation er teknikken, der forvandler statiske websider til dynamiske applikationer, der opdaterer deres indhold baseret på brugerinteraktioner og serverrespons. ### Vælg det rigtige værktøj til opgaven Når det kommer til at opdatere din HTML med JavaScript, har du flere muligheder. Tænk på disse som forskellige værktøjer i en værktøjskasse - hver perfekt til specifikke opgaver: | Metode | Hvad det er godt til | Hvornår man skal bruge det | Sikkerhedsniveau | |--------|----------------------|---------------------------|------------------| | `textContent` | Vise brugerdata sikkert | Når som helst du viser tekst | ✅ Stensikkert | | `createElement()` + `append()` | Bygge komplekse layouts | Oprette nye sektioner/lister | ✅ Skudsikkert | | `innerHTML` | Indstille HTML-indhold | ⚠️ Prøv at undgå denne | ❌ Risikabelt | #### Den sikre måde at vise tekst på: textContent Egenskaben [`textContent`](https://developer.mozilla.org/docs/Web/API/Node/textContent) er din bedste ven, når du viser brugerdata. Det er som at have en dørmand for din webside - intet skadeligt slipper igennem: ```javascript // The safe, reliable way to update text const balanceElement = document.getElementById('balance'); balanceElement.textContent = account.balance; ``` **Fordele ved textContent:** - Behandler alt som almindelig tekst (forhindrer script-eksekvering) - Rydder automatisk eksisterende indhold - Effektivt til enkle tekstopdateringer - Giver indbygget sikkerhed mod skadeligt indhold #### Oprettelse af dynamiske HTML-elementer For mere komplekse indhold, kombiner [`document.createElement()`](https://developer.mozilla.org/docs/Web/API/Document/createElement) med metoden [`append()`](https://developer.mozilla.org/docs/Web/API/ParentNode/append): ```javascript // Safe way to create new elements const transactionItem = document.createElement('div'); transactionItem.className = 'transaction-item'; transactionItem.textContent = `${transaction.date}: ${transaction.description}`; container.append(transactionItem); ``` **Forstå denne tilgang:** - **Opretter** nye DOM-elementer programmatisk - **Giver** fuld kontrol over elementattributter og indhold - **Muliggør** komplekse, indlejrede elementstrukturer - **Bevarer** sikkerheden ved at adskille struktur fra indhold > ⚠️ **Sikkerhedsovervejelse**: Selvom [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML) ofte ses i tutorials, kan det udføre indlejrede scripts. Ligesom sikkerhedsprotokollerne på CERN, der forhindrer uautoriseret kodeudførelse, giver brugen af `textContent` og `createElement` sikrere alternativer. > **Risici ved innerHTML:** - Udfører eventuelle `