# Byg en bank-app del 3: Metoder til at hente og bruge data Tænk på Enterprises computer i Star Trek – når kaptajn Picard spørger efter skibets status, dukker informationen straks op uden at hele interfacet lukker ned og genopbygger sig selv. Den fejlfri informationsstrøm er præcis det, vi bygger her med dynamisk datahentning. Lige nu er din bankapp som en trykt avis – informativ, men statisk. Vi vil forvandle den til noget mere som NASA's mission control, hvor data strømmer kontinuerligt og opdateres i realtid uden at afbryde brugerens arbejdsflow. Du vil lære at kommunikere asynkront med servere, håndtere data, der ankommer på forskellige tidspunkter, og omdanne rå information til noget meningsfuldt for dine brugere. Det er forskellen på en demo og et produktionsklart software. ## ⚡ Hvad du kan nå på de næste 5 minutter **Hurtigstartvej for travle udviklere** ```mermaid flowchart LR A[⚡ 5 minutter] --> B[Opsæt API-server] B --> C[Test hent med curl] C --> D[Opret loginfunktion] D --> E[Se data i aktion] ``` - **Minut 1-2**: Start din API-server (`cd api && npm start`) og test forbindelsen - **Minut 3**: Opret en basal `getAccount()` funktion ved brug af fetch - **Minut 4**: Kobl loginformularen til med `action="javascript:login()"` - **Minut 5**: Test login og se kontodata dukke op i konsollen **Hurtige testkommandoer**: ```bash # Bekræft, at API kører curl http://localhost:5000/api # Test hentning af kontodata curl http://localhost:5000/api/accounts/test ``` **Hvorfor det betyder noget**: På 5 minutter vil du opleve magien ved asynkron datahentning, som driver alle moderne webapplikationer. Det er fundamentet, der får apps til at føles hurtige og levende. ## 🗺️ Din læringsrejse gennem datadrevne webapplikationer ```mermaid journey title Fra statiske sider til dynamiske applikationer section Forstå udviklingen Traditionelle sideopdateringer: 3: You Opdag fordelene ved AJAX/SPA: 5: You Behersk Fetch API-mønstre: 7: You section Opbygning af autentifikation Opret loginfunktioner: 4: You Håndter asynkrone operationer: 6: You Administrer brugersessioner: 8: You section Dynamiske UI-opdateringer Lær DOM-manipulation: 5: You Byg transaktionsvisninger: 7: You Opret responsivt dashboards: 9: You section Professionelle mønstre Skabelonbaseret rendering: 6: You Fejlhåndteringsstrategier: 7: You Ydeevneoptimering: 8: You ``` **Dit læringsmål**: Ved slutningen af denne lektion vil du forstå, hvordan moderne webapplikationer henter, behandler og viser data dynamisk og skaber de fejlfri brugeroplevelser, vi forventer af professionelle applikationer. ## Præ-forelæsning quiz [Præ-forelæsning quiz](https://ff-quizzes.netlify.app/web/quiz/45) ### Forudsætninger Før du dykker ned i datahentning, skal du have disse komponenter klar: - **Forrige lektion**: Færdiggør [Login- og registreringsformularen](../2-forms/README.md) – vi bygger videre på denne base - **Lokal server**: Installer [Node.js](https://nodejs.org) og [kør API-serveren](../api/README.md) for at levere kontodata - **API-forbindelse**: Test din serverforbindelse med denne kommando: ```bash curl http://localhost:5000/api # Forventet svar: "Bank API v1.0.0" ``` Denne hurtige test sikrer, at alle komponenter kommunikerer korrekt: - Verificerer, at Node.js kører korrekt på dit system - Bekræfter at din API-server er aktiv og svarer - Validerer, at din app kan nå serveren (som at tjekke radiokontakt før en mission) ## 🧠 Overblik over datastyringsøkosystemet ```mermaid mindmap root((Data Management)) Authentication Flow Login Process Form Validation Credential Verification Session Management User State Global Account Object Navigation Guards Fejl Håndtering API Communication Fetch Patterns GET Requests POST Requests Fejl Svar Data Formats JSON Processing URL Encoding Response Parsing Dynamic UI Updates DOM Manipulation Safe Text Updates Element Creation Template Cloning User Experience Real-time Updates Fejl Beskeder Loading States Security Considerations XSS Prevention textContent Usage Input Sanitization Safe HTML Creation CORS Handling Cross-Origin Requests Header Configuration Development Setup ``` **Kerneprincip**: Moderne webapplikationer er dataorkestreringssystemer – de koordinerer mellem brugerflader, server-API’er og browserens sikkerhedsmodeller for at skabe fejlfri og responsive oplevelser. --- ## Forståelse af datahentning i moderne webapps Måden webapplikationer håndterer data på har ændret sig dramatisk de sidste to årtier. At forstå denne udvikling hjælper dig med at værdsætte, hvorfor moderne teknikker som AJAX og Fetch API er så kraftfulde, og hvorfor de er blevet uundværlige 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 de tidlige dage af webben var hvert klik som at skifte kanal på et gammelt fjernsyn – skærmen blev sort, og så sad man og ventede på den nye side. Det 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: Klikker på link eller indsender formular Browser->>Server: Anmoder om ny HTML-side Note over Browser: Siden bliver blank Server->>Browser: Returnerer komplet HTML-side Browser->>User: Viser ny side (flash/genindlæsning) ``` ![Opdateringsworkflow i en multi-side applikation](../../../../translated_images/da/mpa.7f7375a1a2d4aa77.webp) **Hvorfor denne tilgang føltes klodset:** - Hvert klik betød, at hele siden skulle bygges op fra bunden - Brugerne blev forstyrret midt i tankerne af de irriterende sideblink - Dit internet arbejdede overtid med at downloade den samme header og footer igen og igen - Apps føltes mere som at bladre i et arkivskab end at bruge software ### Moderne single-page applikationer (SPA) AJAX (Asynchronous JavaScript and XML) ændrede paradigmet fuldstændigt. Ligesom den modulopbyggede design af Den Internationale Rumstation, hvor astronauter kan udskifte enkeltkomponenter uden at bygge hele strukturen om, tillader AJAX os at opdatere specifikke dele af en webside uden at genindlæse hele siden. På trods af navnet, der inkluderer XML, bruger vi mest JSON i dag, men grundprincippet er det samme: opdater kun det, der skal ændres. ```mermaid sequenceDiagram participant User participant Browser participant JavaScript participant Server User->>Browser: Interagerer med side Browser->>JavaScript: Udløser hændelsesbehandler JavaScript->>Server: Henter kun nødvendige data Server->>JavaScript: Returnerer JSON-data JavaScript->>Browser: Opdaterer specifikke sideelementer Browser->>User: Viser opdateret indhold (ingen genindlæsning) ``` ![Opdateringsworkflow i en single-page applikation](../../../../translated_images/da/spa.268ec73b41f992c2.webp) **Hvorfor SPAs føles meget bedre:** - Kun de dele, der rent faktisk ændrer sig, opdateres (smart, ikke?) - Ingen mere forstyrrende afbrydelser – dine brugere forbliver i deres flow - Mindre data over nettet betyder hurtigere indlæsning - Alting 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). Som forskellen på at bruge telegraf og sende email, bruger Fetch API promises for renere asynkron kode og håndterer JSON naturligt. | Funktion | XMLHttpRequest | Fetch API | |---------|----------------|----------| | **Syntax** | Kompleks callback-baseret | Ren promise-baseret | | **JSON-håndtering** | Kræver manuel parsing | Indbygget `.json()` metode | | **Fejlhåndtering** | Begrænset fejlinfo | Omfattende fejlbeskrivelser | | **Moderne support** | Bagudkompatibel | ES6+ promises og async/await | > 💡 **Browserkompatibilitet**: Godt nyt – Fetch API virker i alle moderne browsere! Hvis du er nysgerrig på specifikke versioner, har [caniuse.com](https://caniuse.com/fetch) hele kompatibilitetshistorikken. > **Kort sagt:** - Virker fantastisk i Chrome, Firefox, Safari og Edge (faktisk overalt, hvor dine brugere er) - Kun Internet Explorer har behov for ekstra hjælp (og ærligt talt, det er tid at sige farvel til IE) - Forbereder dig perfekt til elegante async/await-mønstre, vi bruger senere ### Implementering af brugerlogin og datahentning Lad os nu implementere loginsystemet, der forvandler din bankapp fra en statisk visning til en funktionel applikation. Ligesom autentifikationsprotokoller i sikre militære faciliteter, skal vi bekræfte brugerens oplysninger og derefter give adgang til deres specifikke data. Vi bygger dette trinvis, starter med basal autentifikation og tilføjer derefter datahentningsfunktionaliteten. #### Trin 1: Opret grundlaget for login-funktionen Åbn din `app.js` fil og tilføj en ny `login` funktion. Den skal håndtere brugerautentifikationen: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **Lad os bryde det ned:** - Det `async` nøgleord? Det fortæller JavaScript "denne funktion kan komme til at skulle vente på noget" - Vi henter vores form fra siden (intet fancy, bare finder den via dens ID) - Så trækker vi den indtastede brugernavn-værdi ud - Her er et smart trick: du kan få adgang til et hvilket som helst input i en form via dens `name` attribut – ingen behov for ekstra getElementById kald! > 💡 **Form-adgangsmønster**: Alle formkontroller kan tilgås via deres navn (sat i HTML med `name` attributten) som egenskaber på form-elementet. Det giver en ren og læsbar måde at hente formdata på. #### Trin 2: Opret kontodata-hentningsfunktionen Dernæst opretter vi en dedikeret funktion til at hente kontodata fra serveren. Den følger samme mønster som din registreringsfunktion, men fokuserer på dataindhentning: ```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' }; } } ``` **Det her opnår koden:** - **Bruger** det moderne `fetch` API til at anmode om data asynkront - **Bygger** en GET-forespørgsel med brugernavn som parameter - **Anvender** `encodeURIComponent()` for sikkert at håndtere specialtegn i URL’er - **Konverterer** svaret til JSON-format for nem datahåndtering - **Håndterer** fejl elegant ved at returnere et fejlobjekt i stedet for at crashe > ⚠️ **Sikkerhedsnote**: `encodeURIComponent()` håndterer specialtegn i URL’er. Ligesom kodningssystemer brugt i marinekommunikation sikrer den, at din besked ankommer ubeskadiget, og forhindrer at tegn som "#" eller "&" fejltolkes. > **Hvorfor det er vigtigt:** - Forhindrer specialtegn i at bryde URL’er - Beskytter mod URL-manipulationsangreb - Sikrer at serveren modtager de forventede data - Følger sikre udviklingsprincipper #### Forståelse af HTTP GET-forespørgsler Her er noget, der måske overrasker dig: når du bruger `fetch` uden ekstra indstillinger, laver den automatisk en [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET) forespørgsel. Det passer perfekt til, hvad vi laver – spørger serveren "hey, kan jeg se denne brugers kontodata?" Tænk på GET som høfligt at låne en bog på biblioteket – du beder om noget, der allerede findes. POST-forespørgsler (som vi brugte ved registrering) svarer mere til at indsende en ny bog til samlingen. | GET-forespørgsel | POST-forespørgsel | |-------------|-------------| | **Formål** | Hente eksisterende data | Sende nye data til server | | **Parametre** | I URL-sti/spørgestreng | I forespørgselsbody | | **Caching** | Kan caches af browsere | Typisk ikke cached | | **Sikkerhed** | Synlige i URL/logfiler | Skjult i body | ```mermaid sequenceDiagram participant B as Browser participant S as Server Note over B,S: GET Anmodning (Datahentning) B->>S: GET /api/accounts/test S-->>B: 200 OK + Kontodata Note over B,S: POST Anmodning (Dataindsendelse) B->>S: POST /api/accounts + Nye kontodata S-->>B: 201 Oprettet + Bekræftelse Note over B,S: Fejlhåndtering B->>S: GET /api/accounts/nonexistent S-->>B: 404 Ikke fundet + Fejlmeddelelse ``` #### Trin 3: Samling af det hele Nu til den tilfredsstillende del – lad os forbinde din konto-hentningsfunktion med login-processen. Her klikker det hele sammen: ```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 rækkefølge: - Trækker brugernavnet ud fra form-inputtet - Anmoder om brugerkontoens data fra serveren - Håndterer eventuelle fejl undervejs - Gemmer kontodata og navigerer til dashboard ved succes > 🎯 **Async/Await-mønster**: Da `getAccount` er asynkron, bruger vi `await` nøgleordet for at vente på serverens svar, inden koden fortsætter. Det forhindrer, at koden læser udefinerede data. #### Trin 4: Opret et hjem til dine data Din app har brug for et sted at huske kontoinformationen, når den er hentet. Tænk på det som din apps korttidshukommelse – et sted at holde styr på den nuværende brugers data. Tilføj denne linje øverst i din `app.js` fil: ```javascript // Dette indeholder den aktuelle brugers kontodata let account = null; ``` **Hvorfor vi skal bruge det:** - Holder kontodata tilgængelig overalt i appen - Starter med `null`, som betyder "ingen er logget ind endnu" - Opdateres, når en bruger logger ind eller registrerer sig med succes - Fungerer som en enkelt sandhedskilde – ingen forvirring om, hvem der er logget ind #### Trin 5: Kobl din formular til Lad os nu forbinde din flotte nye loginfunktion til din HTML-formular. Opdater dit form-tag sådan her: ```html
``` **Hvad denne lille ændring gør:** - Stopper formen fra at genindlæse hele siden som standard - Kalder din brugerdefinerede JavaScript-funktion i stedet - Holder alt glat og SPA-lignende - Giver dig fuld kontrol over, hvad der sker, når brugerne klikker "Login" #### Trin 6: Forbedr din registreringsfunktion For konsistens, opdater også din `register` funktion til at gemme kontodata og navigere til dashboardet: ```javascript // Tilføj disse linjer i slutningen af din registreringsfunktion account = result; navigate('/dashboard'); ``` **Denne forbedring giver:** - **Problemfri** overgang fra registrering til dashboard - **Konsistent** brugeroplevelse mellem login- og registreringsflow - **Øjeblikkelig** adgang til kontodata efter en succesfuld registrering #### Test af din implementering ```mermaid flowchart TD A[Bruger indtaster legitimationsoplysninger] --> B[Login-funktion kaldt] B --> C[Hent kontodata fra server] C --> D{Data modtaget succesfuldt?} D -->|Ja| E[Gem kontodata globalt] D -->|Nej| F[Vis fejlmeddelelse] E --> G[Naviger til dashboard] F --> H[Bruger forbliver på login-side] ``` **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 oplysninger 3. Kig i browserens konsol (F12) hvis noget virker mærkeligt 4. Bekræft at du kommer ind på dashboardet efter succesfuld login Hvis noget ikke virker, så panik ikke! De fleste problemer er simple fejl som tastefejl eller glemt at starte API-serveren. #### En hurtig bemærkning om Cross-Origin magi Du tænker måske: "Hvordan kan min webapp tale med denne API-server, når de kører på forskellige porte?" Godt spørgsmål! Det kommer vi alle webudviklere til at støde på. > 🔒 **Cross-Origin Sikkerhed**: Browsere håndhæver en "same-origin policy" for at forhindre uautoriseret kommunikation mellem forskellige domæner. Ligesom checkpointsystemet ved Pentagon verifierer de, at kommunikationen er godkendt, før dataoverførsel tillades. > **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), som eksplicit autoriserer kommunikation fra din webapp Denne konfiguration afspejler virkelige udviklingsmiljøer, hvor frontend og backend typisk kører på separate servere. > 📚 **Lær mere**: Dyk dybere ned i API’er og datahentning med denne omfattende [Microsoft Learn modul om API’er](https://docs.microsoft.com/learn/modules/use-apis-discover-museum-art/?WT.mc_id=academic-77807-sagibbon). ## Gør dine data levende i HTML Nu vil vi gøre de hentede data synlige for brugerne via DOM-manipulation. Ligesom processen med at fremkalde fotografier i et mørkekammer, tager vi usynlige data og gengiver dem til noget, brugerne kan se og interagere med. DOM-manipulation er teknikken, der forvandler statiske websider til dynamiske applikationer, som opdaterer deres indhold baseret på brugerinteraktioner og serverrespons. ### Valg af det rigtige værktøj til opgaven Når det kommer til opdatering af dit HTML med JavaScript, har du flere muligheder. Tænk på disse som forskellige værktøjer i en værktøjskasse - hver enkelt perfekt til specifikke opgaver: | Metode | Hvad den er god til | Hvornår den skal bruges | Sikkerhedsniveau | |--------|---------------------|------------------------|------------------| | `textContent` | Sikker visning af brugerdata | Når du viser tekst | ✅ Stærkt som en klippe | | `createElement()` + `append()` | Bygning af komplekse layouts | Oprettelse af nye sektioner/lister | ✅ Bullitproof | | `innerHTML` | Indstilling af 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 til din side – intet skadeligt slipper igennem: ```javascript // Den sikre og pålidelige måde at opdatere tekst på const balanceElement = document.getElementById('balance'); balanceElement.textContent = account.balance; ``` **Fordele ved textContent:** - Behandler al tekst som almindelig tekst (forhindrer script-eksekvering) - Rydder automatisk eksisterende indhold - Effektiv til simple tekstopdateringer - Giver indbygget sikkerhed mod skadeligt indhold #### Oprettelse af dynamiske HTML-elementer For mere komplekst indhold kan du kombinere [`document.createElement()`](https://developer.mozilla.org/docs/Web/API/Document/createElement) med [`append()`](https://developer.mozilla.org/docs/Web/API/ParentNode/append)-metoden: ```javascript // Sikker måde at oprette nye elementer på const transactionItem = document.createElement('div'); transactionItem.className = 'transaction-item'; transactionItem.textContent = `${transaction.date}: ${transaction.description}`; container.append(transactionItem); ``` **Forståelse af denne tilgang:** - **Opretter** nye DOM-elementer programmatisk - **Bevarer** fuld kontrol over elementattributter og indhold - **Tillader** komplekse, indlejrede elementstrukturer - **Bevarer** sikkerheden ved at adskille struktur fra indhold > ⚠️ **Sikkerhedsovervejelse**: Selvom [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML) optræder i mange vejledninger, kan det eksekvere indlejrede scripts. Ligesom sikkerhedsprotokollerne ved CERN, der forhindrer uautoriseret kodeeksekvering, giver brug af `textContent` og `createElement` sikrere alternativer. > **Risici ved innerHTML:** - Eksekverer enhver `