# Bygg en bankapp del 2: Skapa ett inloggnings- och registreringsformulär ## Förhandsquiz [Förhandsquiz](https://ff-quizzes.netlify.app/web/quiz/43) ### Introduktion I nästan alla moderna webbappar kan du skapa ett konto för att få din egen privata plats. Eftersom flera användare kan använda en webbapp samtidigt behöver du en mekanism för att lagra varje användares personliga data separat och välja vilken information som ska visas. Vi kommer inte att gå igenom hur man hanterar [användaridentitet säkert](https://en.wikipedia.org/wiki/Authentication) eftersom det är ett omfattande ämne i sig, men vi kommer att se till att varje användare kan skapa ett (eller flera) bankkonton i vår app. I den här delen kommer vi att använda HTML-formulär för att lägga till inloggning och registrering i vår webbapp. Vi kommer att se hur man skickar data till en server-API programmatiskt och slutligen hur man definierar grundläggande valideringsregler för användarinmatningar. ### Förkunskaper Du behöver ha slutfört [HTML-mallar och routing](../1-template-route/README.md) för webbappen för denna lektion. Du behöver också installera [Node.js](https://nodejs.org) och [köra server-API:t](../api/README.md) lokalt så att du kan skicka data för att skapa konton. **Observera** Du kommer att ha två terminaler igång samtidigt enligt listan nedan: 1. För huvudbankappen vi byggde i lektionen [HTML-mallar och routing](../1-template-route/README.md) 2. För [Bank APP server-API:t](../api/README.md) som vi just satte upp ovan. Du behöver ha båda servrarna igång för att kunna följa resten av lektionen. De lyssnar på olika portar (port `3000` och port `5000`) så allt bör fungera utan problem. Du kan testa att servern körs korrekt genom att köra detta kommando i en terminal: ```sh curl http://localhost:5000/api # -> should return "Bank API v1.0.0" as a result ``` --- ## Formulär och kontroller Elementet `
` kapslar in en sektion av ett HTML-dokument där användaren kan mata in och skicka data med interaktiva kontroller. Det finns alla möjliga användargränssnittskontroller (UI) som kan användas inom ett formulär, den vanligaste är `` och `
``` Med attributet `value` kan vi definiera ett standardvärde för en given inmatning. Observera också att inmatningen för `balance` har typen `number`. Ser det annorlunda ut än de andra inmatningarna? Prova att interagera med det. ✅ Kan du navigera och interagera med formulären med bara ett tangentbord? Hur skulle du göra det? ## Skicka data till servern Nu när vi har ett fungerande gränssnitt är nästa steg att skicka data till vår server. Låt oss göra ett snabbt test med vår nuvarande kod: vad händer om du klickar på knappen *Logga in* eller *Registrera*? Märkte du förändringen i webbläsarens URL-sektion? ![Skärmdump av webbläsarens URL-förändring efter att ha klickat på Registrera-knappen](../../../../translated_images/click-register.e89a30bf0d4bc9ca867dc537c4cea679a7c26368bd790969082f524fed2355bc.sv.png) Standardåtgärden för ett `
` är att skicka formuläret till den aktuella server-URL:en med [GET-metoden](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3), och lägga till formulärdata direkt i URL:en. Denna metod har dock vissa begränsningar: - Den skickade datan är mycket begränsad i storlek (cirka 2000 tecken) - Datan är direkt synlig i URL:en (inte bra för lösenord) - Det fungerar inte med filuppladdningar Därför kan du ändra det till att använda [POST-metoden](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5) som skickar formulärdata till servern i HTTP-förfrågans kropp, utan några av de tidigare begränsningarna. > Även om POST är den mest använda metoden för att skicka data, [i vissa specifika scenarier](https://www.w3.org/2001/tag/doc/whenToUseGet.html) är det att föredra att använda GET-metoden, till exempel när man implementerar ett sökfält. ### Uppgift Lägg till `action` och `method`-egenskaper till registreringsformuläret: ```html ``` Försök nu att registrera ett nytt konto med ditt namn. Efter att ha klickat på knappen *Registrera* bör du se något som detta: ![Ett webbläsarfönster på adressen localhost:5000/api/accounts, som visar en JSON-sträng med användardata](../../../../translated_images/form-post.61de4ca1b964d91a9e338416e19f218504dd0af5f762fbebabfe7ae80edf885f.sv.png) Om allt går bra bör servern svara på din begäran med ett [JSON](https://www.json.org/json-en.html)-svar som innehåller kontodata som skapades. ✅ Försök att registrera igen med samma namn. Vad händer? ## Skicka data utan att ladda om sidan Som du förmodligen märkte finns det ett litet problem med det tillvägagångssätt vi just använde: när vi skickar formuläret lämnar vi vår app och webbläsaren omdirigerar till serverns URL. Vi försöker undvika alla sidomladdningar med vår webbapp, eftersom vi skapar en [Single-page application (SPA)](https://en.wikipedia.org/wiki/Single-page_application). För att skicka formulärdata till servern utan att tvinga en sidomladdning måste vi använda JavaScript-kod. Istället för att sätta en URL i `action`-egenskapen för ett ``-element kan du använda valfri JavaScript-kod som föregås av strängen `javascript:` för att utföra en anpassad åtgärd. Genom att använda detta innebär det också att du måste implementera vissa uppgifter som tidigare gjordes automatiskt av webbläsaren: - Hämta formulärdata - Konvertera och koda formulärdata till ett lämpligt format - Skapa HTTP-förfrågan och skicka den till servern ### Uppgift Byt ut registreringsformulärets `action` med: ```html ``` Öppna `app.js` och lägg till en ny funktion 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); } ``` Här hämtar vi formulärelementet med `getElementById()` och använder hjälpen [`FormData`](https://developer.mozilla.org/docs/Web/API/FormData) för att extrahera värdena från formulärkontroller som en uppsättning nyckel/värde-par. Sedan konverterar vi datan till ett vanligt objekt med [`Object.fromEntries()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) och slutligen serialiserar datan till [JSON](https://www.json.org/json-en.html), ett format som ofta används för att utbyta data på webben. Datan är nu redo att skickas till servern. Skapa en ny funktion 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' }; } } ``` Vad gör denna funktion? Först, observera nyckelordet `async` här. Detta innebär att funktionen innehåller kod som kommer att köras [**asynkront**](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function). När det används tillsammans med nyckelordet `await` tillåter det att vänta på att asynkron kod ska köras - som att vänta på serverns svar här - innan det fortsätter. Här är en snabb video om användning av `async/await`: [![Async och Await för att hantera löften](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk "Async och Await för att hantera löften") > 🎥 Klicka på bilden ovan för en video om async/await. Vi använder `fetch()`-API:t för att skicka JSON-data till servern. Denna metod tar 2 parametrar: - Serverns URL, så vi sätter tillbaka `//localhost:5000/api/accounts` här. - Inställningarna för begäran. Det är där vi ställer in metoden till `POST` och tillhandahåller `body` för begäran. Eftersom vi skickar JSON-data till servern måste vi också ställa in `Content-Type`-headern till `application/json` så att servern vet hur man tolkar innehållet. Eftersom servern kommer att svara på begäran med JSON kan vi använda `await response.json()` för att analysera JSON-innehållet och returnera det resulterande objektet. Observera att denna metod är asynkron, så vi använder nyckelordet `await` här innan vi returnerar för att säkerställa att eventuella fel under analysen också fångas. Lägg nu till lite kod i funktionen `register` för att kalla `createAccount()`: ```js const result = await createAccount(jsonData); ``` Eftersom vi använder nyckelordet `await` här måste vi lägga till nyckelordet `async` före registerfunktionen: ```js async function register() { ``` Slutligen, låt oss lägga till några loggar för att kontrollera resultatet. Den slutliga funktionen bör se ut så här: ```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 lite långt men vi kom dit! Om du öppnar dina [webbläsarutvecklingsverktyg](https://developer.mozilla.org/docs/Learn/Common_questions/What_are_browser_developer_tools) och försöker registrera ett nytt konto bör du inte se någon förändring på webbsidan, men ett meddelande kommer att visas i konsolen som bekräftar att allt fungerar. ![Skärmdump som visar loggmeddelande i webbläsarkonsolen](../../../../translated_images/browser-console.efaf0b51aaaf67782a29e1a0bb32cc063f189b18e894eb5926e02f1abe864ec2.sv.png) ✅ Tror du att datan skickas till servern säkert? Vad händer om någon lyckas avlyssna begäran? Du kan läsa om [HTTPS](https://en.wikipedia.org/wiki/HTTPS) för att lära dig mer om säker datakommunikation. ## Datavalidering Om du försöker registrera ett nytt konto utan att ange ett användarnamn först kan du se att servern returnerar ett fel med statuskod [400 (Bad Request)](https://developer.mozilla.org/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).). Innan du skickar data till en server är det en bra praxis att [validera formulärdata](https://developer.mozilla.org/docs/Learn/Forms/Form_validation) i förväg när det är möjligt, för att säkerställa att du skickar en giltig begäran. HTML5-formulärkontroller tillhandahåller inbyggd validering med olika attribut: - `required`: fältet måste fyllas i, annars kan formuläret inte skickas. - `minlength` och `maxlength`: definierar det minsta och största antalet tecken i textfält. - `min` och `max`: definierar det minsta och största värdet för ett numeriskt fält. - `type`: definierar vilken typ av data som förväntas, som `number`, `email`, `file` eller [andra inbyggda typer](https://developer.mozilla.org/docs/Web/HTML/Element/input). Detta attribut kan också ändra den visuella renderingen av formulärkontrollen. - `pattern`: tillåter att definiera ett [reguljärt uttryck](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Regular_Expressions)-mönster för att testa om den angivna datan är giltig eller inte. > Tips: du kan anpassa utseendet på dina formulärkontroller beroende på om de är giltiga eller ogiltiga genom att använda CSS-pseudoklasserna `:valid` och `:invalid`. ### Uppgift Det finns två obligatoriska fält för att skapa ett giltigt nytt konto: användarnamn och valuta. De andra fälten är valfria. Uppdatera formulärets HTML genom att använda både attributet `required` och text i fältets etikett så att: ```html ... ``` Även om denna specifika serverimplementation inte ställer några specifika gränser för fältens maximala längd, är det alltid en bra praxis att definiera rimliga gränser för all användarinmatning. Lägg till attributet `maxlength` till textfälten: ```html ... ... ``` Om du nu trycker på knappen *Registrera* och ett fält inte följer en valideringsregel som vi har definierat, bör du se något liknande detta: ![Skärmdump som visar valideringsfelet när man försöker skicka formuläret](../../../../translated_images/validation-error.8bd23e98d416c22f80076d04829a4bb718e0e550fd622862ef59008ccf0d5dce.sv.png) Validering som denna, som utförs *innan* någon data skickas till servern, kallas **klientbaserad** validering. Men notera att det inte alltid är möjligt att utföra alla kontroller utan att skicka data. Till exempel kan vi inte här kontrollera om ett konto redan existerar med samma användarnamn utan att skicka en begäran till servern. Ytterligare validering som utförs på servern kallas **serverbaserad** validering. Vanligtvis behöver båda implementeras, och även om klientbaserad validering förbättrar användarupplevelsen genom att ge omedelbar feedback till användaren, är serverbaserad validering avgörande för att säkerställa att användardata som hanteras är korrekt och säker. --- ## 🚀 Utmaning Visa ett felmeddelande i HTML om användaren redan existerar. Här är ett exempel på hur den slutliga inloggningssidan kan se ut efter lite styling: ![Skärmdump av inloggningssidan efter att CSS-stilar har lagts till](../../../../translated_images/result.96ef01f607bf856aa9789078633e94a4f7664d912f235efce2657299becca483.sv.png) ## Quiz efter föreläsningen [Quiz efter föreläsningen](https://ff-quizzes.netlify.app/web/quiz/44) ## Granskning & Självstudier Utvecklare har blivit mycket kreativa när det gäller att bygga formulär, särskilt när det handlar om valideringsstrategier. Lär dig om olika formulärflöden genom att titta igenom [CodePen](https://codepen.com); kan du hitta några intressanta och inspirerande formulär? ## Uppgift [Styla din bankapp](assignment.md) --- **Ansvarsfriskrivning**: Detta dokument har översatts med hjälp av AI-översättningstjänsten [Co-op Translator](https://github.com/Azure/co-op-translator). Även om vi strävar efter noggrannhet, vänligen notera att automatiska översättningar kan innehålla fel eller felaktigheter. Det ursprungliga dokumentet på sitt ursprungliga språk bör betraktas som den auktoritativa källan. För kritisk information rekommenderas professionell mänsklig översättning. Vi ansvarar inte för eventuella missförstånd eller feltolkningar som uppstår vid användning av denna översättning.