# Bygg en bankapp del 3: Metoder för att hämta och använda data Tänk på Enterprise-datorn i Star Trek - när kapten Picard frågar om skeppets status dyker informationen upp direkt utan att hela gränssnittet stängs ner och byggs om. Den sömlösa informationsflödet är precis vad vi bygger här med dynamisk datahämtning. Just nu är din bankapp som en tryckt tidning - informativ men statisk. Vi ska förvandla den till något mer som NASA:s kontrollrum, där data flödar kontinuerligt och uppdateras i realtid utan att avbryta användarens arbetsflöde. Du kommer att lära dig att kommunicera med servrar asynkront, hantera data som anländer vid olika tidpunkter och omvandla rå information till något meningsfullt för dina användare. Detta är skillnaden mellan en demo och produktionsklar mjukvara. ## Förhandsquiz [Förhandsquiz](https://ff-quizzes.netlify.app/web/quiz/45) ### Förutsättningar Innan du dyker in i datahämtning, se till att du har följande komponenter redo: - **Föregående lektion**: Slutför [Inloggnings- och registreringsformuläret](../2-forms/README.md) - vi bygger vidare på denna grund - **Lokal server**: Installera [Node.js](https://nodejs.org) och [kör server-API:t](../api/README.md) för att tillhandahålla kontodata - **API-anslutning**: Testa din serveranslutning med detta kommando: ```bash curl http://localhost:5000/api # Expected response: "Bank API v1.0.0" ``` Detta snabba test säkerställer att alla komponenter kommunicerar korrekt: - Verifierar att Node.js fungerar korrekt på ditt system - Bekräftar att din API-server är aktiv och svarar - Validerar att din app kan nå servern (som att kontrollera radiokontakt före en uppdrag) --- ## Förstå datahämtning i moderna webbappar Sättet som webbapplikationer hanterar data har utvecklats dramatiskt under de senaste två decennierna. Att förstå denna utveckling hjälper dig att uppskatta varför moderna tekniker som AJAX och Fetch API är så kraftfulla och varför de har blivit oumbärliga verktyg för webbutvecklare. Låt oss utforska hur traditionella webbplatser fungerade jämfört med de dynamiska, responsiva applikationer vi bygger idag. ### Traditionella fler-sidiga applikationer (MPA) Under webben tidiga dagar var varje klick som att byta kanal på en gammal TV - skärmen blev svart och sedan kom det nya innehållet långsamt fram. Detta var verkligheten för tidiga webbapplikationer, där varje interaktion innebar att hela sidan byggdes om från grunden. ```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) ```  **Varför denna metod kändes klumpig:** - Varje klick innebar att hela sidan byggdes om från grunden - Användare blev avbrutna mitt i sina tankar av irriterande sidblinkningar - Din internetanslutning arbetade övertid med att ladda ner samma header och footer om och om igen - Appar kändes mer som att bläddra i ett arkivskåp än att använda mjukvara ### Moderna en-sidiga applikationer (SPA) AJAX (Asynchronous JavaScript and XML) förändrade detta paradigm helt. Precis som den modulära designen av den internationella rymdstationen, där astronauter kan byta ut enskilda komponenter utan att bygga om hela strukturen, tillåter AJAX oss att uppdatera specifika delar av en webbsida utan att ladda om allt. Trots att namnet nämner XML använder vi mest JSON idag, men grundprincipen är densamma: uppdatera bara det som behöver ändras. ```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) ```  **Varför SPA:er känns så mycket bättre:** - Endast de delar som faktiskt ändrats uppdateras (smart, eller hur?) - Inga fler störande avbrott - dina användare kan fortsätta i sitt flöde - Mindre data som skickas över nätet innebär snabbare laddning - Allt känns snabbt och responsivt, precis som apparna på din telefon ### Utvecklingen till moderna Fetch API Moderna webbläsare tillhandahåller [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API), som ersätter det äldre [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest). Precis som skillnaden mellan att använda en telegraf och att skicka e-post, använder Fetch API löften för renare asynkron kod och hanterar JSON naturligt. | Funktion | XMLHttpRequest | Fetch API | |----------|----------------|-----------| | **Syntax** | Komplex callback-baserad | Ren promise-baserad | | **JSON-hantering** | Kräver manuell parsing | Inbyggd `.json()`-metod | | **Felkodshantering** | Begränsad felinformation | Omfattande felinformation | | **Modern support** | Kompatibilitet med äldre | ES6+ promises och async/await | > 💡 **Webbläsarkompatibilitet**: Goda nyheter - Fetch API fungerar i alla moderna webbläsare! Om du är nyfiken på specifika versioner, [caniuse.com](https://caniuse.com/fetch) har hela kompatibilitetsberättelsen. > **Slutsats:** - Fungerar utmärkt i Chrome, Firefox, Safari och Edge (i princip överallt där dina användare är) - Endast Internet Explorer behöver extra hjälp (och ärligt talat, det är dags att släppa IE) - Förbereder dig perfekt för de eleganta async/await-mönstren vi kommer att använda senare ### Implementera användarinloggning och datahämtning Nu ska vi implementera inloggningssystemet som förvandlar din bankapp från en statisk display till en funktionell applikation. Precis som autentiseringsprotokollen som används i säkra militära anläggningar, kommer vi att verifiera användarens uppgifter och sedan ge tillgång till deras specifika data. Vi bygger detta stegvis, med början i grundläggande autentisering och sedan lägger till datahämtning. #### Steg 1: Skapa grunden för inloggningsfunktionen Öppna din `app.js`-fil och lägg till en ny `login`-funktion. Denna kommer att hantera användarens autentiseringsprocess: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **Låt oss bryta ner detta:** - Det där `async`-nyckelordet? Det säger till JavaScript "hej, den här funktionen kan behöva vänta på saker" - Vi hämtar vårt formulär från sidan (inget märkvärdigt, bara hittar det via dess ID) - Sedan hämtar vi det användaren har skrivit som sitt användarnamn - Här är ett smart knep: du kan komma åt alla formulärfält via deras `name`-attribut - ingen extra getElementById behövs! > 💡 **Formuläråtkomstmönster**: Varje formulärkontroll kan nås via dess namn (inställt i HTML med `name`-attributet) som en egenskap av formulärelementet. Detta ger ett rent och läsbart sätt att hämta formulärdata. #### Steg 2: Skapa funktionen för att hämta kontodata Nästa steg är att skapa en dedikerad funktion för att hämta kontodata från servern. Detta följer samma mönster som din registreringsfunktion men fokuserar på datahämtning: ```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' }; } } ``` **Vad denna kod gör:** - **Använder** det moderna `fetch`-API:t för att begära data asynkront - **Konstruerar** en GET-förfrågnings-URL med användarnamnsparametern - **Använder** `encodeURIComponent()` för att säkert hantera specialtecken i URL:er - **Konverterar** svaret till JSON-format för enkel datamanipulation - **Hanterar** fel på ett smidigt sätt genom att returnera ett felobjekt istället för att krascha > ⚠️ **Säkerhetsnotering**: Funktionen `encodeURIComponent()` hanterar specialtecken i URL:er. Precis som kodningssystem som används i marin kommunikation säkerställer den att ditt meddelande anländer exakt som avsett, och förhindrar att tecken som "#" eller "&" tolkas fel. > **Varför detta är viktigt:** - Förhindrar att specialtecken bryter URL:er - Skyddar mot URL-manipulationsattacker - Säkerställer att din server får den avsedda datan - Följer säkra kodningspraxis #### Förstå HTTP GET-förfrågningar Här är något som kanske överraskar dig: när du använder `fetch` utan några extra alternativ skapar det automatiskt en [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET)-förfrågan. Detta är perfekt för vad vi gör - att fråga servern "hej, kan jag se denna användares kontodata?" Tänk på GET-förfrågningar som att artigt be att få låna en bok från biblioteket - du begär att få se något som redan finns. POST-förfrågningar (som vi använde för registrering) är mer som att lämna in en ny bok för att läggas till i samlingen. | GET-förfrågan | POST-förfrågan | |---------------|---------------| | **Syfte** | Hämta befintlig data | Skicka ny data till servern | | **Parametrar** | I URL-sökväg/query-sträng | I förfrågningskroppen | | **Caching** | Kan cachas av webbläsare | Inte vanligtvis cachad | | **Säkerhet** | Synlig i URL/loggar | Dold i förfrågningskroppen | #### Steg 3: Koppla ihop allt Nu till den tillfredsställande delen - låt oss koppla din kontohämtningsfunktion till inloggningsprocessen. Här klickar allt på plats: ```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'); } ``` Denna funktion följer en tydlig sekvens: - Extrahera användarnamnet från formulärfältet - Begär användarens kontodata från servern - Hantera eventuella fel som uppstår under processen - Spara kontodatan och navigera till dashboarden vid framgång > 🎯 **Async/Await-mönster**: Eftersom `getAccount` är en asynkron funktion använder vi nyckelordet `await` för att pausa exekveringen tills servern svarar. Detta förhindrar att koden fortsätter med odefinierad data. #### Steg 4: Skapa en plats för din data Din app behöver någonstans att komma ihåg kontoinformationen när den har laddats. Tänk på detta som appens korttidsminne - en plats att hålla den aktuella användarens data tillgänglig. Lägg till denna rad högst upp i din `app.js`-fil: ```javascript // This holds the current user's account data let account = null; ``` **Varför vi behöver detta:** - Håller kontodatan tillgänglig från var som helst i din app - Att börja med `null` betyder "ingen är inloggad än" - Uppdateras när någon loggar in eller registrerar sig framgångsrikt - Fungerar som en enda sanningskälla - ingen förvirring om vem som är inloggad #### Steg 5: Koppla din formulär Nu ska vi koppla din nya inloggningsfunktion till ditt HTML-formulär. Uppdatera din formulärtagg så här: ```html
``` **Vad denna lilla ändring gör:** - Stoppar formuläret från att göra sitt standardbeteende "ladda om hela sidan" - Anropar din anpassade JavaScript-funktion istället - Håller allt smidigt och SPA-likt - Ger dig full kontroll över vad som händer när användare trycker på "Login" #### Steg 6: Förbättra din registreringsfunktion För konsekvens, uppdatera din `register`-funktion för att också spara kontodata och navigera till dashboarden: ```javascript // Add these lines at the end of your register function account = result; navigate('/dashboard'); ``` **Denna förbättring ger:** - **Sömlös** övergång från registrering till dashboard - **Konsekvent** användarupplevelse mellan inloggnings- och registreringsflöden - **Omedelbar** åtkomst till kontodata efter framgångsrik registrering #### Testa din implementation ```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] ``` **Dags att testa:** 1. Skapa ett nytt konto för att se till att allt fungerar 2. Försök logga in med samma uppgifter 3. Titta i webbläsarens konsol (F12) om något verkar fel 4. Se till att du landar på dashboarden efter en lyckad inloggning Om något inte fungerar, få inte panik! De flesta problem är enkla att lösa, som stavfel eller att glömma att starta API-servern. #### Ett snabbt ord om Cross-Origin magi Du kanske undrar: "Hur pratar min webbapp med denna API-server när de körs på olika portar?" Bra fråga! Detta berör något som varje webbutvecklare stöter på förr eller senare. > 🔒 **Cross-Origin-säkerhet**: Webbläsare upprätthåller en "same-origin policy" för att förhindra obehörig kommunikation mellan olika domäner. Precis som kontrollsystemet vid Pentagon verifierar de att kommunikationen är auktoriserad innan dataöverföring tillåts. > **I vår setup:** - Din webbapp körs på `localhost:3000` (utvecklingsserver) - Din API-server körs på `localhost:5000` (backend-server) - API-servern inkluderar [CORS-headers](https://developer.mozilla.org/docs/Web/HTTP/CORS) som uttryckligen tillåter kommunikation från din webbapp Denna konfiguration speglar verklig utveckling där frontend- och backend-applikationer vanligtvis körs på separata servrar. > 📚 **Läs mer**: Fördjupa dig i API:er och datahämtning med denna omfattande [Microsoft Learn-modul om API:er](https://docs.microsoft.com/learn/modules/use-apis-discover-museum-art/?WT.mc_id=academic-77807-sagibbon). ## Ge liv åt din data i HTML Nu ska vi göra den hämtade datan synlig för användarna genom DOM-manipulation. Precis som processen att framkalla fotografier i ett mörkrum, tar vi osynlig data och renderar den till något användarna kan se och interagera med. DOM-manipulation är tekniken som förvandlar statiska webbsidor till dynamiska applikationer som uppdaterar sitt innehåll baserat på användarinteraktioner och serverrespons. ### Välja rätt verktyg för jobbet När det gäller att uppdatera din HTML med JavaScript har du flera alternativ. Tänk på dessa som olika verktyg i en verktygslåda - varje perfekt för specifika uppgifter: | Metod | Vad den är bra för | När du ska använda den | Säkerhetsnivå | |-------|--------------------|------------------------|---------------| | `textContent` | Visa användardata säkert | När du visar text | ✅ Stensäker | | `createElement()` + `append()` | Bygga komplexa layouter | Skapa nya sektioner/listor | ✅ Skottsäker | | `innerHTML` | Ställa in HTML-innehåll | ⚠️ Försök undvika denna | ❌ Riskabelt | #### Det säkra sättet att visa text: textContent Egenskapen [`textContent`](https://developer.mozilla.org/docs/Web/API/Node/textContent) är din bästa vän när du visar användardata. Det är som att ha en dörrvakt för din webbsida - inget skadligt släpps igenom: ```javascript // The safe, reliable way to update text const balanceElement = document.getElementById('balance'); balanceElement.textContent = account.balance; ``` **Fördelar med textContent:** - Behandlar allt som vanlig text (förhindrar skriptkörning) - Rensar automatiskt befintligt innehåll - Effektivt för enkla textuppdateringar - Ger inbyggd säkerhet mot skadligt innehåll #### Skapa dynamiska HTML-element För mer komplexa innehåll, kombinera [`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); ``` **Förstå denna metod:** - **Skapar** nya DOM-element programmässigt - **Ger** full kontroll över elementens attribut och innehåll - **Möjliggör** komplexa, nästlade elementstrukturer - **Bevarar** säkerheten genom att separera struktur från innehåll > ⚠️ **Säkerhetsövervägande**: Även om [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML) ofta förekommer i tutorials, kan det exekvera inbäddade skript. Precis som säkerhetsprotokollen på CERN som förhindrar obehörig kodexekvering, erbjuder `textContent` och `createElement` säkrare alternativ. > **Risker med innerHTML:** - Exekverar alla `