# Een bankapp bouwen Deel 3: Methoden voor het ophalen en gebruiken van gegevens Denk aan de computer van de Enterprise in Star Trek - wanneer Captain Picard vraagt om de status van het schip, verschijnt de informatie direct zonder dat de hele interface opnieuw moet worden opgebouwd. Die naadloze informatiestroom is precies wat we hier proberen te bouwen met dynamisch ophalen van gegevens. Op dit moment is jouw bankapp als een gedrukt dagblad - informatief maar statisch. We gaan het transformeren naar iets dat lijkt op de missiecontrole van NASA, waar gegevens continu stromen en in real-time worden bijgewerkt zonder de workflow van de gebruiker te onderbreken. Je leert hoe je asynchroon met servers kunt communiceren, hoe je gegevens kunt verwerken die op verschillende momenten binnenkomen, en hoe je ruwe informatie kunt omzetten in iets betekenisvols voor je gebruikers. Dit is het verschil tussen een demo en software die klaar is voor productie. ## Quiz voorafgaand aan de les [Quiz voorafgaand aan de les](https://ff-quizzes.netlify.app/web/quiz/45) ### Vereisten Voordat we beginnen met het ophalen van gegevens, zorg ervoor dat je de volgende onderdelen klaar hebt: - **Vorige les**: Voltooi het [Login- en registratieformulier](../2-forms/README.md) - we bouwen hierop voort - **Lokale server**: Installeer [Node.js](https://nodejs.org) en [start de server-API](../api/README.md) om accountgegevens te leveren - **API-verbinding**: Test je serververbinding met dit commando: ```bash curl http://localhost:5000/api # Expected response: "Bank API v1.0.0" ``` Deze snelle test zorgt ervoor dat alle componenten correct communiceren: - Verifieert dat Node.js correct werkt op je systeem - Bevestigt dat je API-server actief is en reageert - Valideert dat je app de server kan bereiken (zoals het controleren van radiocontact voor een missie) --- ## Begrijpen van gegevens ophalen in moderne webapps De manier waarop webapplicaties gegevens verwerken is de afgelopen twee decennia drastisch veranderd. Het begrijpen van deze evolutie helpt je te waarderen waarom moderne technieken zoals AJAX en de Fetch API zo krachtig zijn en waarom ze essentiële tools zijn geworden voor webontwikkelaars. Laten we eens kijken hoe traditionele websites werkten in vergelijking met de dynamische, responsieve applicaties die we vandaag de dag bouwen. ### Traditionele Multi-Page Applicaties (MPA) In de begindagen van het web was elke klik alsof je van kanaal wisselde op een oude televisie - het scherm werd zwart en stemde langzaam af op de nieuwe inhoud. Dit was de realiteit van vroege webapplicaties, waarbij elke interactie betekende dat de hele pagina opnieuw moest worden opgebouwd. ```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) ``` ![Update workflow in een multi-page applicatie](../../../../translated_images/mpa.7f7375a1a2d4aa779d3f928a2aaaf9ad76bcdeb05cfce2dc27ab126024050f51.nl.png) **Waarom deze aanpak onhandig aanvoelde:** - Elke klik betekende dat de hele pagina opnieuw moest worden opgebouwd - Gebruikers werden midden in hun gedachten onderbroken door die vervelende paginaflitsen - Je internetverbinding werkte overuren om steeds dezelfde header en footer opnieuw te downloaden - Apps voelden meer als bladeren door een archiefkast dan als het gebruik van software ### Moderne Single-Page Applicaties (SPA) AJAX (Asynchronous JavaScript and XML) veranderde dit paradigma volledig. Net zoals het modulaire ontwerp van het internationale ruimtestation, waar astronauten individuele componenten kunnen vervangen zonder de hele structuur opnieuw op te bouwen, stelt AJAX ons in staat om specifieke delen van een webpagina bij te werken zonder alles opnieuw te laden. Hoewel de naam XML noemt, gebruiken we tegenwoordig meestal JSON, maar het kernprincipe blijft hetzelfde: alleen bijwerken wat moet veranderen. ```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) ``` ![Update workflow in een single-page applicatie](../../../../translated_images/spa.268ec73b41f992c2a21ef9294235c6ae597b3c37e2c03f0494c2d8857325cc57.nl.png) **Waarom SPAs zoveel beter aanvoelen:** - Alleen de delen die daadwerkelijk zijn veranderd worden bijgewerkt (slim, toch?) - Geen schokkende onderbrekingen meer - je gebruikers blijven in hun flow - Minder gegevens die over de lijn reizen betekent snellere laadtijden - Alles voelt snel en responsief, zoals de apps op je telefoon ### De evolutie naar de moderne Fetch API Moderne browsers bieden de [`Fetch` API](https://developer.mozilla.org/docs/Web/API/Fetch_API), die de oudere [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest) vervangt. Net zoals het verschil tussen het bedienen van een telegraaf en het gebruik van e-mail, gebruikt de Fetch API promises voor schonere asynchrone code en verwerkt JSON van nature. | Functie | XMLHttpRequest | Fetch API | |---------|----------------|-----------| | **Syntax** | Complex, op callbacks gebaseerd | Schoon, op promises gebaseerd | | **JSON-verwerking** | Handmatige parsing vereist | Ingebouwde `.json()`-methode | | **Foutafhandeling** | Beperkte foutinformatie | Uitgebreide foutdetails | | **Moderne ondersteuning** | Compatibiliteit met oudere systemen | ES6+ promises en async/await | > 💡 **Browsercompatibiliteit**: Goed nieuws - de Fetch API werkt in alle moderne browsers! Als je nieuwsgierig bent naar specifieke versies, [caniuse.com](https://caniuse.com/fetch) heeft het volledige compatibiliteitsverhaal. > **Kort samengevat:** - Werkt uitstekend in Chrome, Firefox, Safari en Edge (praktisch overal waar je gebruikers zijn) - Alleen Internet Explorer heeft extra hulp nodig (en eerlijk gezegd, het is tijd om afscheid te nemen van IE) - Bereidt je perfect voor op de elegante async/await-patronen die we later zullen gebruiken ### Implementatie van gebruikerslogin en gegevens ophalen Laten we nu het inlogsysteem implementeren dat je bankapp transformeert van een statische weergave naar een functionele applicatie. Net zoals de authenticatieprotocollen die worden gebruikt in beveiligde militaire faciliteiten, verifiëren we gebruikersgegevens en bieden we vervolgens toegang tot hun specifieke gegevens. We bouwen dit stapsgewijs op, te beginnen met basisauthenticatie en vervolgens het toevoegen van mogelijkheden voor gegevens ophalen. #### Stap 1: Maak de basis voor de loginfunctie Open je `app.js`-bestand en voeg een nieuwe `login`-functie toe. Deze zal het authenticatieproces van de gebruiker afhandelen: ```javascript async function login() { const loginForm = document.getElementById('loginForm'); const user = loginForm.user.value; } ``` **Laten we dit ontleden:** - Dat `async`-trefwoord? Het vertelt JavaScript "hé, deze functie moet misschien even wachten" - We halen ons formulier van de pagina (niets bijzonders, gewoon vinden via zijn ID) - Vervolgens halen we op wat de gebruiker heeft ingevoerd als gebruikersnaam - Hier is een handige truc: je kunt elk formulierinvoer bereiken via zijn `name`-attribuut - geen extra getElementById-aanroepen nodig! > 💡 **Formulier toegangspatroon**: Elke formuliercontrole kan worden benaderd via zijn naam (ingesteld in de HTML met het `name`-attribuut) als een eigenschap van het formelement. Dit biedt een schone, leesbare manier om formuliergegevens op te halen. #### Stap 2: Maak de functie voor het ophalen van accountgegevens Vervolgens maken we een speciale functie om accountgegevens van de server op te halen. Dit volgt hetzelfde patroon als je registratiefunctie, maar richt zich op het ophalen van gegevens: ```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' }; } } ``` **Wat deze code doet:** - **Gebruikt** de moderne `fetch` API om gegevens asynchroon op te vragen - **Stelt** een GET-verzoek-URL samen met de gebruikersnaamparameter - **Past** `encodeURIComponent()` toe om speciale tekens in URL's veilig te verwerken - **Converteert** de respons naar JSON-formaat voor eenvoudige gegevensmanipulatie - **Handelt** fouten netjes af door een foutobject te retourneren in plaats van te crashen > ⚠️ **Beveiligingsopmerking**: De functie `encodeURIComponent()` verwerkt speciale tekens in URL's. Net zoals coderingssystemen in maritieme communicatie, zorgt het ervoor dat je bericht precies aankomt zoals bedoeld, en voorkomt dat tekens zoals "#" of "&" verkeerd worden geïnterpreteerd. > **Waarom dit belangrijk is:** - Voorkomt dat speciale tekens URL's breken - Beschermt tegen aanvallen waarbij URL's worden gemanipuleerd - Zorgt ervoor dat je server de bedoelde gegevens ontvangt - Volgt veilige coderingspraktijken #### Begrijpen van HTTP GET-verzoeken Hier is iets dat je misschien verrast: wanneer je `fetch` gebruikt zonder extra opties, maakt het automatisch een [`GET`](https://developer.mozilla.org/docs/Web/HTTP/Methods/GET)-verzoek. Dit is perfect voor wat we doen - de server vragen "hé, mag ik de accountgegevens van deze gebruiker zien?" Denk aan GET-verzoeken als beleefd vragen om een boek te lenen uit de bibliotheek - je vraagt om iets te zien dat al bestaat. POST-verzoeken (die we gebruikten voor registratie) zijn meer zoals het indienen van een nieuw boek om aan de collectie toe te voegen. | GET-verzoek | POST-verzoek | |-------------|--------------| | **Doel** | Bestaande gegevens ophalen | Nieuwe gegevens naar de server sturen | | **Parameters** | In URL-pad/querystring | In de request body | | **Caching** | Kan worden gecached door browsers | Wordt meestal niet gecached | | **Beveiliging** | Zichtbaar in URL/logs | Verborgen in de request body | #### Stap 3: Alles samenbrengen Nu komt het bevredigende deel - laten we je functie voor het ophalen van accountgegevens koppelen aan het inlogproces. Hier komt alles samen: ```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'); } ``` Deze functie volgt een duidelijke volgorde: - Haal de gebruikersnaam uit de formulierinvoer - Vraag de accountgegevens van de gebruiker op bij de server - Handel eventuele fouten af die tijdens het proces optreden - Sla de accountgegevens op en navigeer naar het dashboard bij succes > 🎯 **Async/Await-patroon**: Omdat `getAccount` een asynchrone functie is, gebruiken we het `await`-trefwoord om de uitvoering te pauzeren totdat de server reageert. Dit voorkomt dat de code doorgaat met ongedefinieerde gegevens. #### Stap 4: Een plek creëren voor je gegevens Je app heeft een plek nodig om de accountinformatie te onthouden zodra deze is geladen. Denk aan dit als het kortetermijngeheugen van je app - een plek om de gegevens van de huidige gebruiker bij de hand te houden. Voeg deze regel toe aan de bovenkant van je `app.js`-bestand: ```javascript // This holds the current user's account data let account = null; ``` **Waarom we dit nodig hebben:** - Houdt de accountgegevens toegankelijk vanuit elke plek in je app - Beginnen met `null` betekent "niemand is nog ingelogd" - Wordt bijgewerkt wanneer iemand succesvol inlogt of registreert - Werkt als een enkele bron van waarheid - geen verwarring over wie er is ingelogd #### Stap 5: Verbind je formulier Laten we nu je gloednieuwe inlogfunctie verbinden met je HTML-formulier. Werk je formuliertag als volgt bij: ```html
``` **Wat deze kleine verandering doet:** - Stopt het formulier van zijn standaardgedrag om de hele pagina opnieuw te laden - Roept in plaats daarvan je aangepaste JavaScript-functie aan - Houdt alles soepel en single-page-app-achtig - Geeft je volledige controle over wat er gebeurt wanneer gebruikers op "Login" klikken #### Stap 6: Verbeter je registratiefunctie Voor consistentie, werk je `register`-functie bij om ook accountgegevens op te slaan en naar het dashboard te navigeren: ```javascript // Add these lines at the end of your register function account = result; navigate('/dashboard'); ``` **Deze verbetering biedt:** - **Naadloze** overgang van registratie naar dashboard - **Consistente** gebruikerservaring tussen inlog- en registratieprocessen - **Directe** toegang tot accountgegevens na succesvolle registratie #### Test je implementatie ```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] ``` **Tijd om het uit te proberen:** 1. Maak een nieuw account om te controleren of alles werkt 2. Probeer in te loggen met dezelfde inloggegevens 3. Kijk in de console van je browser (F12) als er iets niet klopt 4. Zorg ervoor dat je op het dashboard terechtkomt na een succesvolle login Als er iets niet werkt, geen paniek! De meeste problemen zijn eenvoudig op te lossen, zoals typfouten of vergeten de API-server te starten. #### Een kort woord over cross-origin magie Je vraagt je misschien af: "Hoe communiceert mijn webapp met deze API-server terwijl ze op verschillende poorten draaien?" Goede vraag! Dit raakt aan iets waar elke webontwikkelaar uiteindelijk mee te maken krijgt. > 🔒 **Cross-Origin Security**: Browsers handhaven een "same-origin policy" om ongeautoriseerde communicatie tussen verschillende domeinen te voorkomen. Net zoals het controlesysteem bij het Pentagon, verifiëren ze dat communicatie is geautoriseerd voordat gegevensoverdracht wordt toegestaan. > **In onze setup:** - Je webapp draait op `localhost:3000` (ontwikkelserver) - Je API-server draait op `localhost:5000` (backendserver) - De API-server bevat [CORS headers](https://developer.mozilla.org/docs/Web/HTTP/CORS) die expliciet communicatie van je webapp autoriseren Deze configuratie weerspiegelt de echte wereld, waar frontend- en backendapplicaties meestal op afzonderlijke servers draaien. > 📚 **Meer leren**: Verdiep je verder in API's en gegevens ophalen met deze uitgebreide [Microsoft Learn module over API's](https://docs.microsoft.com/learn/modules/use-apis-discover-museum-art/?WT.mc_id=academic-77807-sagibbon). ## Je gegevens tot leven brengen in HTML Nu gaan we de opgehaalde gegevens zichtbaar maken voor gebruikers via DOM-manipulatie. Net zoals het ontwikkelen van foto's in een donkere kamer, nemen we onzichtbare gegevens en renderen we ze tot iets dat gebruikers kunnen zien en ermee kunnen interageren. DOM-manipulatie is de techniek die statische webpagina's transformeert in dynamische applicaties die hun inhoud bijwerken op basis van gebruikersinteracties en serverreacties. ### Het juiste gereedschap kiezen voor de klus Als het gaat om het bijwerken van je HTML met JavaScript, heb je verschillende opties. Denk aan deze als verschillende gereedschappen in een gereedschapskist - elk perfect voor specifieke taken: | Methode | Waar het goed voor is | Wanneer te gebruiken | Veiligheidsniveau | |---------|-----------------------|----------------------|-------------------| | `textContent` | Veilig weergeven van gebruikersgegevens | Altijd wanneer je tekst toont | ✅ Zeer veilig | | `createElement()` + `append()` | Complexe layouts bouwen | Nieuwe secties/lijsten maken | ✅ Zeer betrouwbaar | | `innerHTML` | HTML-inhoud instellen | ⚠️ Probeer dit te vermijden | ❌ Risicovol | #### De veilige manier om tekst te tonen: textContent De [`textContent`](https://developer.mozilla.org/docs/Web/API/Node/textContent)-eigenschap is je beste vriend bij het weergeven van gebruikersgegevens. Het is alsof je een portier hebt voor je webpagina - niets schadelijks komt erdoor: ```javascript // The safe, reliable way to update text const balanceElement = document.getElementById('balance'); balanceElement.textContent = account.balance; ``` **Voordelen van textContent:** - Behandelt alles als gewone tekst (voorkomt scriptuitvoering) - Leegt automatisch bestaande inhoud - Efficiënt voor eenvoudige tekstupdates - Biedt ingebouwde beveiliging tegen schadelijke inhoud #### Dynamische HTML-elementen maken Voor complexere inhoud kun je [`document.createElement()`](https://developer.mozilla.org/docs/Web/API/Document/createElement) combineren met de methode [`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); ``` **Begrijp deze aanpak:** - **Maakt** nieuwe DOM-elementen op een programmeerbare manier - **Biedt** volledige controle over de attributen en inhoud van elementen - **Maakt** complexe, geneste structuren mogelijk - **Waarborgt** veiligheid door structuur en inhoud te scheiden > ⚠️ **Veiligheidsoverweging**: Hoewel [`innerHTML`](https://developer.mozilla.org/docs/Web/API/Element/innerHTML) vaak voorkomt in tutorials, kan het ingesloten scripts uitvoeren. Net zoals de veiligheidsprotocollen bij CERN ongeautoriseerde code-uitvoering voorkomen, bieden `textContent` en `createElement` veiligere alternatieven. > **Risico's van innerHTML:** - Voert eventuele `