# Bygg en bankapp del 3: Metoder for henting og bruk av data Tenk på Enterprise-datamaskinen i Star Trek – når kaptein Picard spør om skipets status, vises informasjonen umiddelbart uten at hele grensesnittet må lukkes og bygges opp på nytt. Den sømløse informasjonsflyten er akkurat det vi skal bygge her med dynamisk datahenting. Akkurat nå er bankappen din som en trykt avis – informativ, men statisk. Vi skal forvandle den til noe mer som NASAs kontrollsenter, der data flyter kontinuerlig og oppdateres i sanntid uten å avbryte brukerens arbeidsflyt. Du vil lære hvordan du kommuniserer med servere asynkront, håndterer data som ankommer til forskjellige tider, og transformerer rå informasjon til noe meningsfullt for brukerne dine. Dette er forskjellen mellom en demo og programvare som er klar for produksjon. ## Quiz før forelesning [Quiz før forelesning](https://ff-quizzes.netlify.app/web/quiz/45) ### Forutsetninger Før du dykker inn i datahenting, sørg for at du har disse komponentene klare: - **Forrige leksjon**: Fullfør [Innloggings- og registreringsskjemaet](../2-forms/README.md) – vi bygger videre på dette grunnlaget - **Lokal server**: Installer [Node.js](https://nodejs.org) og [kjør server-API](../api/README.md) for å levere kontodata - **API-tilkobling**: Test servertilkoblingen din med denne kommandoen: ```bash curl http://localhost:5000/api # Expected response: "Bank API v1.0.0" ``` Denne raske testen sikrer at alle komponenter kommuniserer riktig: - Bekrefter at Node.js kjører som det skal på systemet ditt - Bekrefter at API-serveren din er aktiv og svarer - Validerer at appen din kan nå serveren (som å sjekke radiokontakt før en oppdrag) --- ## Forstå datahenting i moderne webapplikasjoner Måten webapplikasjoner håndterer data på har utviklet seg dramatisk de siste to tiårene. Å forstå denne utviklingen vil hjelpe deg å sette pris på hvorfor moderne teknikker som AJAX og Fetch API er så kraftige og hvorfor de har blitt essensielle verktøy for webutviklere. La oss utforske hvordan tradisjonelle nettsteder fungerte sammenlignet med de dynamiske, responsive applikasjonene vi bygger i dag. ### Tradisjonelle fler-sides applikasjoner (MPA) I internettets tidlige dager var hvert klikk som å skifte kanal på en gammel TV – skjermen ble blank, og deretter kom det nye innholdet sakte frem. Dette var realiteten for tidlige webapplikasjoner, der hver interaksjon betydde at hele siden måtte bygges opp på nytt fra bunnen av. ```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 tilnærmingen føltes klønete:** - Hvert klikk betydde at hele siden måtte bygges opp på nytt - Brukere ble avbrutt midt i tanken av irriterende sideblinker - Internettforbindelsen din jobbet overtid med å laste ned den samme topp- og bunnteksten gjentatte ganger - Apper føltes mer som å bla gjennom et arkivskap enn å bruke programvare ### Moderne én-sides applikasjoner (SPA) AJAX (Asynchronous JavaScript and XML) endret dette paradigmet fullstendig. Som den modulære designen til den internasjonale romstasjonen, der astronauter kan bytte ut individuelle komponenter uten å bygge hele strukturen på nytt, lar AJAX oss oppdatere spesifikke deler av en nettside uten å laste alt på nytt. Selv om navnet nevner XML, bruker vi stort sett JSON i dag, men hovedprinsippet er det samme: oppdater bare det som trenger å endres. ```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å mye bedre:** - Bare de delene som faktisk endres blir oppdatert (smart, ikke sant?) - Ingen flere brå avbrytelser – brukerne dine holder seg i flyten - Mindre data som sendes over nettet betyr raskere lasting - Alt føles raskt og responsivt, som appene på telefonen din ### Utviklingen til moderne Fetch API Moderne nettlesere tilbyr [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API), som erstatter den eldre [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest). Som forskjellen mellom å bruke en telegraf og e-post, bruker Fetch API løfter for renere asynkron kode og håndterer JSON naturlig. | Funksjon | XMLHttpRequest | Fetch API | |----------|----------------|-----------| | **Syntaks** | Kompleks callback-basert | Ren promise-basert | | **JSON-håndtering** | Krever manuell parsing | Innebygd `.json()`-metode | | **Feilhåndtering** | Begrenset feilinformasjons | Omfattende feildetaljer | | **Moderne støtte** | Kompatibilitet med eldre systemer | ES6+ løfter og async/await | > 💡 **Nettleserkompatibilitet**: Gode nyheter – Fetch API fungerer i alle moderne nettlesere! Hvis du er nysgjerrig på spesifikke versjoner, har [caniuse.com](https://caniuse.com/fetch) hele kompatibilitetshistorien. > **Konklusjon:** - Fungerer utmerket i Chrome, Firefox, Safari og Edge (i utgangspunktet overalt hvor brukerne dine er) - Bare Internet Explorer trenger ekstra hjelp (og ærlig talt, det er på tide å gi slipp på IE) - Setter deg perfekt opp for de elegante async/await-mønstrene vi skal bruke senere ### Implementering av brukerinnlogging og datahenting La oss nå implementere innloggingssystemet som forvandler bankappen din fra en statisk visning til en funksjonell applikasjon. Som autentiseringsprotokollene som brukes i sikre militæranlegg, vil vi verifisere brukerens legitimasjon og deretter gi tilgang til deres spesifikke data. Vi bygger dette trinnvis, med start i grunnleggende autentisering og deretter legge til datahentingsfunksjoner. #### Trinn 1: Lag grunnlaget for innloggingsfunksjonen Åpne `app.js`-filen din og legg til en ny `login`-funksjon. Denne vil håndtere brukerens autentiseringsprosess: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **La oss bryte dette ned:** - Det `async` nøkkelordet? Det forteller JavaScript "hei, denne funksjonen kan trenge å vente på ting" - Vi henter skjemaet vårt fra siden (ikke noe fancy, bare finner det ved hjelp av ID-en) - Deretter henter vi det brukeren har skrevet inn som brukernavn - Her er et smart triks: du kan få tilgang til alle skjemaelementer via `name`-attributtet – ingen grunn til å bruke ekstra getElementById-kall! > 💡 **Mønster for skjema-tilgang**: Hvert skjemaelement kan nås via navnet (satt i HTML med `name`-attributtet) som en egenskap av skjemaelementet. Dette gir en ren og lesbar måte å hente skjema-data på. #### Trinn 2: Lag en funksjon for henting av kontodata Neste steg er å lage en dedikert funksjon for å hente kontodata fra serveren. Dette følger samme mønster som registreringsfunksjonen din, men fokuserer på datahenting: ```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' }; } } ``` **Dette oppnår koden:** - **Bruker** det moderne `fetch` API for å be om data asynkront - **Konstruerer** en GET-forespørsel med brukernavnparameter - **Bruker** `encodeURIComponent()` for å håndtere spesialtegn i URL-er på en sikker måte - **Konverterer** responsen til JSON-format for enkel datamanipulasjon - **Håndterer** feil på en ryddig måte ved å returnere et feilobjekt i stedet for å krasje > ⚠️ **Sikkerhetsnotat**: `encodeURIComponent()`-funksjonen håndterer spesialtegn i URL-er. Som kodingssystemene som brukes i militær kommunikasjon, sikrer den at meldingen din kommer frem akkurat som den skal, og hindrer tegn som "#" eller "&" fra å bli feiltolket. > **Hvorfor dette er viktig:** - Hindrer spesialtegn fra å ødelegge URL-er - Beskytter mot angrep som manipulerer URL-er - Sikrer at serveren mottar de tiltenkte dataene - Følger sikre kodingspraksiser #### Forstå HTTP GET-forespørsler Her er noe som kanskje overrasker deg: når du bruker `fetch` uten ekstra alternativer, oppretter den automatisk en [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET)-forespørsel. Dette er perfekt for det vi gjør – spør serveren "hei, kan jeg se denne brukerens kontodata?" Tenk på GET-forespørsler som å høflig be om å låne en bok fra biblioteket – du ber om å se noe som allerede eksisterer. POST-forespørsler (som vi brukte for registrering) er mer som å sende inn en ny bok for å bli lagt til i samlingen. | GET-forespørsel | POST-forespørsel | |-----------------|------------------| | **Formål** | Hente eksisterende data | Sende nye data til serveren | | **Parametere** | I URL-sti/spørringsstreng | I forespørselens kropp | | **Caching** | Kan caches av nettlesere | Ikke typisk cached | | **Sikkerhet** | Synlig i URL/logg | Skjult i forespørselens kropp | #### Trinn 3: Sette alt sammen Nå til den tilfredsstillende delen – la oss koble kontohentingsfunksjonen din til innloggingsprosessen. Dette er der alt faller på plass: ```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 funksjonen følger en klar sekvens: - Henter brukernavnet fra skjemaets input - Ber om brukerens kontodata fra serveren - Håndterer eventuelle feil som oppstår under prosessen - Lagrer kontodataene og navigerer til dashbordet ved suksess > 🎯 **Async/Await-mønster**: Siden `getAccount` er en asynkron funksjon, bruker vi nøkkelordet `await` for å pause utførelsen til serveren svarer. Dette hindrer koden fra å fortsette med udefinerte data. #### Trinn 4: Lag et sted for dataene dine Appen din trenger et sted å huske kontoinformasjonen når den er lastet. Tenk på dette som appens korttidsminne – et sted å holde den nåværende brukerens data tilgjengelig. Legg til denne linjen øverst i `app.js`-filen din: ```javascript // This holds the current user's account data let account = null; ``` **Hvorfor vi trenger dette:** - Holder kontodataene tilgjengelige fra hvor som helst i appen din - Å starte med `null` betyr "ingen er logget inn ennå" - Oppdateres når noen logger inn eller registrerer seg - Fungerer som en enkelt sannhetskilde – ingen forvirring om hvem som er logget inn #### Trinn 5: Koble skjemaet ditt La oss nå koble den nye innloggingsfunksjonen din til HTML-skjemaet. Oppdater skjema-taggen din slik: ```html
``` **Hva denne lille endringen gjør:** - Stopper skjemaet fra å gjøre sin standard "last hele siden på nytt"-oppførsel - Kaller din tilpassede JavaScript-funksjon i stedet - Holder alt jevnt og én-sides-app-lignende - Gir deg full kontroll over hva som skjer når brukere trykker på "Logg inn" #### Trinn 6: Forbedre registreringsfunksjonen din For konsistens, oppdater `register`-funksjonen din til også å lagre kontodata og navigere til dashbordet: ```javascript // Add these lines at the end of your register function account = result; navigate('/dashboard'); ``` **Denne forbedringen gir:** - **Sømløs** overgang fra registrering til dashbord - **Konsekvent** brukeropplevelse mellom innlogging og registreringsflyt - **Umiddelbar** tilgang til kontodata etter vellykket registrering #### Testing av implementeringen ```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 for å teste:** 1. Opprett en ny konto for å sikre at alt fungerer 2. Prøv å logge inn med de samme legitimasjonene 3. Sjekk nettleserens konsoll (F12) hvis noe virker galt 4. Sørg for at du lander på dashbordet etter en vellykket innlogging Hvis noe ikke fungerer, ikke få panikk! De fleste problemer er enkle å fikse, som skrivefeil eller å glemme å starte API-serveren. #### Et raskt ord om Cross-Origin magi Du lurer kanskje: "Hvordan snakker webappen min med denne API-serveren når de kjører på forskjellige porter?" Godt spørsmål! Dette berører noe som enhver webutvikler før eller siden møter. > 🔒 **Cross-Origin Security**: Nettlesere håndhever en "same-origin policy" for å forhindre uautorisert kommunikasjon mellom forskjellige domener. Som kontrollsystemet ved Pentagon, verifiserer de at kommunikasjonen er autorisert før dataoverføring tillates. > **I vår oppsett:** - Webappen din kjører på `localhost:3000` (utviklingsserver) - API-serveren din kjører på `localhost:5000` (backend-server) - API-serveren inkluderer [CORS-headere](https://developer.mozilla.org/docs/Web/HTTP/CORS) som eksplisitt autoriserer kommunikasjon fra webappen din Denne konfigurasjonen speiler virkelige utviklingsmiljøer der frontend- og backend-applikasjoner vanligvis kjører på separate servere. > 📚 **Lær mer**: Utforsk API-er og datahenting mer inngående med dette omfattende [Microsoft Learn-modulet om API-er](https://docs.microsoft.com/learn/modules/use-apis-discover-museum-art/?WT.mc_id=academic-77807-sagibbon). ## Gjør dataene dine levende i HTML Nå skal vi gjøre de hentede dataene synlige for brukerne gjennom DOM-manipulasjon. Som prosessen med å fremkalle fotografier i et mørkerom, tar vi usynlige data og gjengir dem til noe brukerne kan se og samhandle med. DOM-manipulasjon er teknikken som forvandler statiske nettsider til dynamiske applikasjoner som oppdaterer innholdet basert på brukerinteraksjoner og serverresponser. ### Velge riktig verktøy for jobben Når det gjelder å oppdatere HTML med JavaScript, har du flere alternativer. Tenk på disse som forskjellige verktøy i en verktøykasse – hver av dem er perfekt for spesifikke oppgaver: | Metode | Hva den er bra for | Når du skal bruke den | Sikkerhetsnivå | |--------|--------------------|-----------------------|----------------| | `textContent` | Vise brukerdata på en sikker måte | Når du viser tekst | ✅ Svært sikker | | `createElement()` + `append()` | Bygge komplekse oppsett | Lage nye seksjoner/lister | ✅ Svært sikker | | `innerHTML` | Sette HTML-innhold | ⚠️ Prøv å unngå denne | ❌ Risikabelt | #### Den sikre måten å vise tekst: textContent [`textContent`](https://developer.mozilla.org/docs/Web/API/Node/textContent)-egenskapen er din beste venn når du viser brukerdata. Det er som å ha en dørvakt for nettsiden din – ingenting skadelig slipper gjennom: ```javascript // The safe, reliable way to update text const balanceElement = document.getElementById('balance'); balanceElement.textContent = account.balance; ``` **Fordeler med textContent:** - Behandler alt som ren tekst (hindrer skriptutførelse) - Tømmer automatisk eksisterende innhold - Effektiv for enkle tekstoppdateringer - Gir innebygd sikkerhet mot skadelig innhold #### Lage dynamiske HTML-elementer For mer kompleks innhold, 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 tilnærmingen:** - **Oppretter** nye DOM-elementer programmatisk - **Gir** full kontroll over elementattributter og innhold - **Tillater** komplekse, nestede elementstrukturer - **Bevarer** sikkerheten ved å skille struktur fra innhold > ⚠️ **Sikkerhetsbetraktning**: Selv om [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML) ofte vises i mange opplæringsguider, kan det utføre innebygde skript. Akkurat som sikkerhetsprotokollene på CERN som forhindrer uautorisert kodeutførelse, gir bruk av `textContent` og `createElement` sikrere alternativer. > **Risiko ved innerHTML:** - Utfører eventuelle `