# Bygg en bankapp Del 2: Lag et innloggings- og registreringsskjema ## Forhåndsquiz [Forhåndsquiz](https://ff-quizzes.netlify.app/web/quiz/43) ### Introduksjon I nesten alle moderne webapplikasjoner kan du opprette en konto for å få ditt eget private område. Siden flere brukere kan få tilgang til en webapplikasjon samtidig, trenger du en mekanisme for å lagre hver brukers personlige data separat og velge hvilken informasjon som skal vises. Vi kommer ikke til å dekke hvordan man håndterer [brukeridentitet sikkert](https://en.wikipedia.org/wiki/Authentication), da det er et omfattende tema i seg selv, men vi skal sørge for at hver bruker kan opprette én (eller flere) bankkontoer i appen vår. I denne delen skal vi bruke HTML-skjemaer for å legge til innlogging og registrering i webapplikasjonen vår. Vi skal se hvordan vi kan sende data til en server-API programmessig, og til slutt hvordan vi kan definere grunnleggende valideringsregler for brukerinput. ### Forutsetninger Du må ha fullført [HTML-maler og ruting](../1-template-route/README.md) for webapplikasjonen i denne leksjonen. Du må også installere [Node.js](https://nodejs.org) og [kjøre server-API-et](../api/README.md) lokalt slik at du kan sende data for å opprette kontoer. **Merk deg dette** Du vil ha to terminaler kjørende samtidig som beskrevet nedenfor: 1. For hovedbankappen vi bygde i leksjonen [HTML-maler og ruting](../1-template-route/README.md) 2. For [Bank APP server-API-et](../api/README.md) som vi nettopp satte opp. Du må ha begge serverne oppe og kjørende for å kunne følge resten av leksjonen. De lytter på forskjellige porter (port `3000` og port `5000`), så alt skal fungere fint. Du kan teste at serveren kjører riktig ved å kjøre denne kommandoen i en terminal: ```sh curl http://localhost:5000/api # -> should return "Bank API v1.0.0" as a result ``` --- ## Skjema og kontroller `
`-elementet kapsler inn en seksjon av et HTML-dokument der brukeren kan skrive inn og sende data med interaktive kontroller. Det finnes mange forskjellige brukergrensesnittkontroller (UI-kontroller) som kan brukes i et skjema, hvor de vanligste er `` og `
``` Ved å bruke attributtet `value` kan vi definere en standardverdi for et gitt felt. Legg også merke til at feltet for `balance` har typen `number`. Ser det annerledes ut enn de andre feltene? Prøv å samhandle med det. ✅ Kan du navigere og samhandle med skjemaene kun ved hjelp av tastaturet? Hvordan ville du gjort det? ## Sende data til serveren Nå som vi har et funksjonelt brukergrensesnitt, er neste steg å sende dataene til serveren. La oss gjøre en rask test med vår nåværende kode: Hva skjer hvis du klikker på *Logg inn*- eller *Registrer*-knappen? La du merke til endringen i nettleserens URL-seksjon? ![Skjermbilde av nettleserens URL-endring etter å ha klikket på Registrer-knappen](../../../../translated_images/click-register.e89a30bf0d4bc9ca867dc537c4cea679a7c26368bd790969082f524fed2355bc.no.png) Standardhandlingen for et `
` er å sende skjemaet til den nåværende server-URL-en ved hjelp av [GET-metoden](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3), og legge til skjemadataene direkte i URL-en. Denne metoden har imidlertid noen begrensninger: - Dataene som sendes er svært begrenset i størrelse (ca. 2000 tegn) - Dataene er direkte synlige i URL-en (ikke ideelt for passord) - Den fungerer ikke med filopplastinger Derfor kan du endre den til å bruke [POST-metoden](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5), som sender skjemadataene til serveren i HTTP-forespørselens kropp, uten noen av de tidligere begrensningene. > Selv om POST er den mest brukte metoden for å sende data, [i noen spesifikke scenarier](https://www.w3.org/2001/tag/doc/whenToUseGet.html) er det bedre å bruke GET-metoden, for eksempel når du implementerer et søkefelt. ### Oppgave Legg til `action`- og `method`-egenskaper i registreringsskjemaet: ```html ``` Prøv nå å registrere en ny konto med navnet ditt. Etter å ha klikket på *Registrer*-knappen, bør du se noe som dette: ![Et nettleservindu på adressen localhost:5000/api/accounts, som viser en JSON-streng med brukerdata](../../../../translated_images/form-post.61de4ca1b964d91a9e338416e19f218504dd0af5f762fbebabfe7ae80edf885f.no.png) Hvis alt går bra, skal serveren svare på forespørselen din med en [JSON](https://www.json.org/json-en.html)-respons som inneholder kontodataene som ble opprettet. ✅ Prøv å registrere deg igjen med samme navn. Hva skjer? ## Sende data uten å laste siden på nytt Som du sikkert la merke til, er det et lite problem med tilnærmingen vi nettopp brukte: Når skjemaet sendes, forlater vi appen vår, og nettleseren omdirigerer til serverens URL. Vi prøver å unngå alle sideoppdateringer i webapplikasjonen vår, siden vi lager en [Single-page application (SPA)](https://en.wikipedia.org/wiki/Single-page_application). For å sende skjemadataene til serveren uten å tvinge en sideoppdatering, må vi bruke JavaScript-kode. I stedet for å sette en URL i `action`-egenskapen til et ``-element, kan du bruke hvilken som helst JavaScript-kode med prefikset `javascript:` for å utføre en egendefinert handling. Dette betyr også at du må implementere noen oppgaver som tidligere ble gjort automatisk av nettleseren: - Hente skjemadataene - Konvertere og kode skjemadataene til et passende format - Opprette HTTP-forespørselen og sende den til serveren ### Oppgave Erstatt `action` i registreringsskjemaet med: ```html ``` Åpne `app.js` og legg til en ny funksjon som heter `register`: ```js function register() { const registerForm = document.getElementById('registerForm'); const formData = new FormData(registerForm); const data = Object.fromEntries(formData); const jsonData = JSON.stringify(data); } ``` Her henter vi skjemaelementet ved hjelp av `getElementById()` og bruker [`FormData`](https://developer.mozilla.org/docs/Web/API/FormData)-hjelperen for å hente verdiene fra skjemakontroller som et sett med nøkkel/verdi-par. Deretter konverterer vi dataene til et vanlig objekt ved hjelp av [`Object.fromEntries()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) og til slutt serialiserer dataene til [JSON](https://www.json.org/json-en.html), et format som ofte brukes for å utveksle data på nettet. Dataene er nå klare til å sendes til serveren. Lag en ny funksjon som heter `createAccount`: ```js async function createAccount(account) { try { const response = await fetch('//localhost:5000/api/accounts', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: account }); return await response.json(); } catch (error) { return { error: error.message || 'Unknown error' }; } } ``` Hva gjør denne funksjonen? Først, merk nøkkelordet `async` her. Dette betyr at funksjonen inneholder kode som vil kjøre [**asynkront**](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function). Når det brukes sammen med nøkkelordet `await`, lar det oss vente på at asynkron kode skal kjøre - som å vente på serverresponsen her - før vi fortsetter. Her er en rask video om bruk av `async/await`: [![Async og Await for håndtering av løfter](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk "Async og Await for håndtering av løfter") > 🎥 Klikk på bildet over for en video om async/await. Vi bruker `fetch()`-API-et for å sende JSON-data til serveren. Denne metoden tar 2 parametere: - URL-en til serveren, så vi setter tilbake `//localhost:5000/api/accounts` her. - Innstillingene for forespørselen. Det er her vi setter metoden til `POST` og oppgir `body` for forespørselen. Siden vi sender JSON-data til serveren, må vi også sette `Content-Type`-headeren til `application/json` slik at serveren vet hvordan den skal tolke innholdet. Siden serveren vil svare på forespørselen med JSON, kan vi bruke `await response.json()` for å analysere JSON-innholdet og returnere det resulterende objektet. Merk at denne metoden er asynkron, så vi bruker nøkkelordet `await` her før vi returnerer for å sikre at eventuelle feil under analysen også fanges opp. Legg nå til litt kode i `register`-funksjonen for å kalle `createAccount()`: ```js const result = await createAccount(jsonData); ``` Siden vi bruker nøkkelordet `await` her, må vi legge til nøkkelordet `async` før register-funksjonen: ```js async function register() { ``` Til slutt, la oss legge til noen logger for å sjekke resultatet. Den endelige funksjonen skal se slik ut: ```js async function register() { const registerForm = document.getElementById('registerForm'); const formData = new FormData(registerForm); const jsonData = JSON.stringify(Object.fromEntries(formData)); const result = await createAccount(jsonData); if (result.error) { return console.log('An error occurred:', result.error); } console.log('Account created!', result); } ``` Det var litt langt, men vi kom i mål! Hvis du åpner [nettleserens utviklerverktøy](https://developer.mozilla.org/docs/Learn/Common_questions/What_are_browser_developer_tools) og prøver å registrere en ny konto, bør du ikke se noen endring på nettsiden, men en melding vil vises i konsollen som bekrefter at alt fungerer. ![Skjermbilde som viser loggmelding i nettleserkonsollen](../../../../translated_images/browser-console.efaf0b51aaaf67782a29e1a0bb32cc063f189b18e894eb5926e02f1abe864ec2.no.png) ✅ Tror du dataene sendes til serveren sikkert? Hva om noen klarte å fange opp forespørselen? Du kan lese om [HTTPS](https://en.wikipedia.org/wiki/HTTPS) for å lære mer om sikker datakommunikasjon. ## Datavalidering Hvis du prøver å registrere en ny konto uten å angi et brukernavn først, kan du se at serveren returnerer en feil med statuskode [400 (Bad Request)](https://developer.mozilla.org/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).). Før du sender data til en server, er det en god praksis å [validere skjemadataene](https://developer.mozilla.org/docs/Learn/Forms/Form_validation) på forhånd når det er mulig, for å sikre at du sender en gyldig forespørsel. HTML5-skjemaelementer gir innebygd validering ved hjelp av ulike attributter: - `required`: Feltet må fylles ut, ellers kan ikke skjemaet sendes. - `minlength` og `maxlength`: Definerer minimums- og maksimumsantall tegn i tekstfelt. - `min` og `max`: Definerer minimums- og maksimumsverdi for et numerisk felt. - `type`: Definerer hvilken type data som forventes, som `number`, `email`, `file` eller [andre innebygde typer](https://developer.mozilla.org/docs/Web/HTML/Element/input). Dette attributtet kan også endre den visuelle gjengivelsen av skjemakontrollen. - `pattern`: Lar deg definere et [regulært uttrykk](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Regular_Expressions)-mønster for å teste om de oppgitte dataene er gyldige eller ikke. > Tips: Du kan tilpasse utseendet til skjemakontrollene dine avhengig av om de er gyldige eller ikke, ved å bruke CSS-pseudoklassene `:valid` og `:invalid`. ### Oppgave Det er to obligatoriske felt for å opprette en gyldig ny konto: brukernavn og valuta. De andre feltene er valgfrie. Oppdater HTML-skjemaet ved å bruke både `required`-attributtet og tekst i feltets etikett slik at: ```html ... ``` Selv om denne spesifikke serverimplementasjonen ikke håndhever spesifikke grenser for maksimal lengde på feltene, er det alltid en god praksis å definere rimelige grenser for tekstinnskriving fra brukeren. Legg til et `maxlength`-attributt i tekstfeltene: ```html ... ... ``` Nå, hvis du trykker på *Registrer* og et felt ikke oppfyller en valideringsregel vi har definert, vil du se noe som dette: ![Skjermbilde som viser valideringsfeil når man prøver å sende inn skjemaet](../../../../translated_images/validation-error.8bd23e98d416c22f80076d04829a4bb718e0e550fd622862ef59008ccf0d5dce.no.png) Validering som dette, utført *før* data sendes til serveren, kalles **klientsidevalidering**. Men merk at det ikke alltid er mulig å utføre alle sjekker uten å sende data. For eksempel kan vi ikke her sjekke om en konto allerede eksisterer med samme brukernavn uten å sende en forespørsel til serveren. Ytterligere validering utført på serveren kalles **serversidevalidering**. Vanligvis må begge implementeres, og mens klientsidevalidering forbedrer brukeropplevelsen ved å gi umiddelbar tilbakemelding til brukeren, er serversidevalidering avgjørende for å sikre at brukerdataene du håndterer er pålitelige og sikre. --- ## 🚀 Utfordring Vis en feilmelding i HTML hvis brukeren allerede eksisterer. Her er et eksempel på hvordan den endelige innloggingssiden kan se ut etter litt styling: ![Skjermbilde av innloggingssiden etter å ha lagt til CSS-stiler](../../../../translated_images/result.96ef01f607bf856aa9789078633e94a4f7664d912f235efce2657299becca483.no.png) ## Quiz etter forelesning [Quiz etter forelesning](https://ff-quizzes.netlify.app/web/quiz/44) ## Gjennomgang og selvstudium Utviklere har blitt svært kreative når det gjelder å bygge skjemaer, spesielt med tanke på valideringsstrategier. Lær om ulike skjemaoppsett ved å se gjennom [CodePen](https://codepen.com); kan du finne noen interessante og inspirerende skjemaer? ## Oppgave [Style din bankapp](assignment.md) --- **Ansvarsfraskrivelse**: Dette dokumentet er oversatt ved hjelp av AI-oversettelsestjenesten [Co-op Translator](https://github.com/Azure/co-op-translator). Selv om vi streber etter nøyaktighet, vær oppmerksom på at automatiserte oversettelser kan inneholde feil eller unøyaktigheter. Det originale dokumentet på sitt opprinnelige språk bør anses som den autoritative kilden. For kritisk informasjon anbefales profesjonell menneskelig oversettelse. Vi er ikke ansvarlige for misforståelser eller feiltolkninger som oppstår ved bruk av denne oversettelsen.