You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/sv/7-bank-project/3-data
leestott 425e0f6fbe
🌐 Update translations via Co-op Translator
3 weeks ago
..
README.md 🌐 Update translations via Co-op Translator 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 4 weeks ago

README.md

Bygg en bankapp Del 3: Metoder för att hämta och använda data

Förkunskapstest

Förkunskapstest

Introduktion

I kärnan av varje webbapplikation finns data. Data kan ta många former, men dess huvudsakliga syfte är alltid att visa information för användaren. Med webbappar som blir alltmer interaktiva och komplexa är hur användaren får tillgång till och interagerar med information nu en nyckeldel av webbutveckling.

I den här lektionen kommer vi att se hur man hämtar data från en server asynkront och använder dessa data för att visa information på en webbsida utan att ladda om HTML.

Förutsättningar

Du behöver ha byggt inloggnings- och registreringsformuläret som en del av webbappen för den här lektionen. Du behöver också installera Node.js och köra server-API:et lokalt för att få kontodata.

Du kan testa att servern fungerar korrekt genom att köra följande kommando i en terminal:

curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result

AJAX och datahämtning

Traditionella webbplatser uppdaterar innehållet som visas när användaren väljer en länk eller skickar data via ett formulär genom att ladda om hela HTML-sidan. Varje gång nya data behöver laddas returnerar webbservern en helt ny HTML-sida som måste bearbetas av webbläsaren, vilket avbryter användarens aktuella åtgärd och begränsar interaktioner under omladdningen. Detta arbetsflöde kallas också för en Multi-Page Application eller MPA.

Uppdateringsflöde i en fler-sidars applikation

När webbapplikationer började bli mer komplexa och interaktiva uppstod en ny teknik som kallas AJAX (Asynchronous JavaScript and XML). Denna teknik gör det möjligt för webbappar att skicka och hämta data från en server asynkront med hjälp av JavaScript, utan att behöva ladda om HTML-sidan, vilket resulterar i snabbare uppdateringar och smidigare användarinteraktioner. När nya data tas emot från servern kan den aktuella HTML-sidan också uppdateras med JavaScript med hjälp av DOM-API:et. Med tiden har detta tillvägagångssätt utvecklats till vad som nu kallas en Single-Page Application eller SPA.

Uppdateringsflöde i en en-sidars applikation

När AJAX först introducerades var det enda API som fanns tillgängligt för att hämta data asynkront XMLHttpRequest. Men moderna webbläsare implementerar nu också det mer praktiska och kraftfulla Fetch API, som använder promises och är bättre lämpat för att hantera JSON-data.

Även om alla moderna webbläsare stöder Fetch API, är det alltid en bra idé att kontrollera kompatibilitetstabellen på caniuse.com först om du vill att din webbapplikation ska fungera på äldre webbläsare.

Uppgift

I den föregående lektionen implementerade vi registreringsformuläret för att skapa ett konto. Nu ska vi lägga till kod för att logga in med ett befintligt konto och hämta dess data. Öppna filen app.js och lägg till en ny funktion login:

async function login() {
  const loginForm = document.getElementById('loginForm')
  const user = loginForm.user.value;
}

Här börjar vi med att hämta formulärelementet med getElementById() och sedan hämtar vi användarnamnet från inmatningen med loginForm.user.value. Varje formulärkontroll kan nås via sitt namn (angivet i HTML med attributet name) som en egenskap av formuläret.

På liknande sätt som vi gjorde för registreringen, skapar vi en annan funktion för att utföra en serverförfrågan, men den här gången för att hämta kontodata:

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' };
  }
}

Vi använder fetch-API:et för att begära data asynkront från servern, men den här gången behöver vi inga extra parametrar förutom URL:en att anropa, eftersom vi bara frågar efter data. Som standard skapar fetch en GET-HTTP-förfrågan, vilket är vad vi söker här.

encodeURIComponent() är en funktion som kodar specialtecken för URL:er. Vilka problem skulle vi kunna stöta på om vi inte anropar denna funktion och använder värdet user direkt i URL:en?

Låt oss nu uppdatera vår login-funktion för att använda getAccount:

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');
}

Först, eftersom getAccount är en asynkron funktion, måste vi använda nyckelordet await för att vänta på serverresultatet. Som med alla serverförfrågningar måste vi också hantera fel. För tillfället lägger vi bara till ett loggmeddelande för att visa felet och återkommer till det senare.

Sedan måste vi lagra data någonstans så att vi senare kan använda den för att visa information på instrumentpanelen. Eftersom variabeln account ännu inte existerar, skapar vi en global variabel för den högst upp i vår fil:

let account = null;

Efter att användardata har sparats i en variabel kan vi navigera från login-sidan till dashboard med hjälp av funktionen navigate() som vi redan har.

Slutligen måste vi anropa vår login-funktion när inloggningsformuläret skickas, genom att ändra HTML:

<form id="loginForm" action="javascript:login()">

Testa att allt fungerar korrekt genom att registrera ett nytt konto och försöka logga in med samma konto.

Innan vi går vidare till nästa del kan vi också komplettera register-funktionen genom att lägga till detta längst ner i funktionen:

account = result;
navigate('/dashboard');

Visste du att du som standard bara kan anropa server-API:er från samma domän och port som webbsidan du tittar på? Detta är en säkerhetsmekanism som webbläsare upprätthåller. Men vänta, vår webbapp körs på localhost:3000 medan server-API:et körs på localhost:5000, varför fungerar det? Genom att använda en teknik som kallas Cross-Origin Resource Sharing (CORS) är det möjligt att utföra cross-origin HTTP-förfrågningar om servern lägger till speciella headers i svaret, vilket tillåter undantag för specifika domäner.

Lär dig mer om API:er genom att ta denna lektion

Uppdatera HTML för att visa data

Nu när vi har användardata måste vi uppdatera den befintliga HTML:en för att visa den. Vi vet redan hur man hämtar ett element från DOM med till exempel document.getElementById(). Efter att du har ett bas-element finns här några API:er du kan använda för att ändra det eller lägga till underordnade element:

  • Med egenskapen textContent kan du ändra texten i ett element. Observera att ändring av detta värde tar bort alla elementets barn (om det finns några) och ersätter det med den angivna texten. Därför är det också en effektiv metod för att ta bort alla barn till ett givet element genom att tilldela en tom sträng '' till det.

  • Med document.createElement() tillsammans med metoden append() kan du skapa och fästa ett eller flera nya underordnade element.

Med egenskapen innerHTML för ett element är det också möjligt att ändra dess HTML-innehåll, men denna bör undvikas eftersom den är sårbar för cross-site scripting (XSS)-attacker.

Uppgift

Innan vi går vidare till instrumentpanelen finns det en sak till vi bör göra på login-sidan. För närvarande, om du försöker logga in med ett användarnamn som inte existerar, visas ett meddelande i konsolen, men för en vanlig användare händer inget och du vet inte vad som pågår.

Låt oss lägga till ett platshållarelement i inloggningsformuläret där vi kan visa ett felmeddelande om det behövs. En bra plats skulle vara precis före inloggningsknappen <button>:

...
<div id="loginError"></div>
<button>Login</button>
...

Detta <div>-element är tomt, vilket innebär att inget kommer att visas på skärmen förrän vi lägger till något innehåll i det. Vi ger det också ett id så att vi enkelt kan hämta det med JavaScript.

Gå tillbaka till filen app.js och skapa en ny hjälpfunktion updateElement:

function updateElement(id, text) {
  const element = document.getElementById(id);
  element.textContent = text;
}

Den här är ganska enkel: givet ett element-id och text kommer den att uppdatera textinnehållet i DOM-elementet med det matchande id. Låt oss använda denna metod istället för det tidigare felmeddelandet i login-funktionen:

if (data.error) {
  return updateElement('loginError', data.error);
}

Nu, om du försöker logga in med ett ogiltigt konto, bör du se något som detta:

Skärmdump som visar felmeddelandet under inloggning

Nu har vi ett felmeddelande som visas visuellt, men om du testar det med en skärmläsare kommer du att märka att inget meddelas. För att text som läggs till dynamiskt på en sida ska meddelas av skärmläsare måste den använda något som kallas en Live Region. Här ska vi använda en specifik typ av live-region som kallas en alert:

<div id="loginError" role="alert"></div>

Implementera samma beteende för fel i register-funktionen (glöm inte att uppdatera HTML).

Visa information på instrumentpanelen

Med hjälp av samma tekniker som vi just har sett ska vi också ta hand om att visa kontoinformationen på instrumentpanelsidan.

Så här ser ett kontoobjekt som tas emot från servern ut:

{
  "user": "test",
  "currency": "$",
  "description": "Test account",
  "balance": 75,
  "transactions": [
    { "id": "1", "date": "2020-10-01", "object": "Pocket money", "amount": 50 },
    { "id": "2", "date": "2020-10-03", "object": "Book", "amount": -10 },
    { "id": "3", "date": "2020-10-04", "object": "Sandwich", "amount": -5 }
  ],
}

Obs: för att göra ditt liv enklare kan du använda det fördefinierade kontot test som redan är fyllt med data.

Uppgift

Låt oss börja med att ersätta avsnittet "Balance" i HTML för att lägga till platshållarelement:

<section>
  Balance: <span id="balance"></span><span id="currency"></span>
</section>

Vi lägger också till ett nytt avsnitt precis nedanför för att visa kontobeskrivningen:

<h2 id="description"></h2>

Eftersom kontobeskrivningen fungerar som en titel för innehållet under den, är den semantiskt markerad som en rubrik. Lär dig mer om hur rubrikstruktur är viktig för tillgänglighet och granska sidan kritiskt för att avgöra vad mer som kan vara en rubrik.

Nästa steg är att skapa en ny funktion i app.js för att fylla i platshållaren:

function updateDashboard() {
  if (!account) {
    return navigate('/login');
  }

  updateElement('description', account.description);
  updateElement('balance', account.balance.toFixed(2));
  updateElement('currency', account.currency);
}

Först kontrollerar vi att vi har de kontodata vi behöver innan vi går vidare. Sedan använder vi funktionen updateElement() som vi skapade tidigare för att uppdatera HTML.

För att göra saldodisplayen snyggare använder vi metoden toFixed(2) för att tvinga fram visning av värdet med 2 decimaler.

Nu behöver vi anropa vår updateDashboard()-funktion varje gång instrumentpanelen laddas. Om du redan har slutfört uppgiften i lektion 1 bör detta vara enkelt, annars kan du använda följande implementation.

Lägg till denna kod i slutet av funktionen updateRoute():

if (typeof route.init === 'function') {
  route.init();
}

Och uppdatera ruttdefinitionerna med:

const routes = {
  '/login': { templateId: 'login' },
  '/dashboard': { templateId: 'dashboard', init: updateDashboard }
};

Med denna ändring kommer funktionen updateDashboard() att anropas varje gång instrumentpanelsidan visas. Efter en inloggning bör du då kunna se kontosaldot, valutan och beskrivningen.

Skapa tabellrader dynamiskt med HTML-mallar

I den första lektionen använde vi HTML-mallar tillsammans med metoden appendChild() för att implementera navigeringen i vår app. Mallar kan också vara mindre och användas för att dynamiskt fylla repetitiva delar av en sida.

Vi kommer att använda ett liknande tillvägagångssätt för att visa listan över transaktioner i HTML-tabellen.

Uppgift

Lägg till en ny mall i HTML-<body>:

<template id="transaction">
  <tr>
    <td></td>
    <td></td>
    <td></td>
  </tr>
</template>

Denna mall representerar en enda tabellrad med de tre kolumner vi vill fylla: datum, objekt och belopp för en transaktion.

Lägg sedan till denna id-egenskap i <tbody>-elementet i tabellen inom instrumentpanelsmallen för att göra det enklare att hitta med JavaScript:

<tbody id="transactions"></tbody>

Vår HTML är redo, låt oss byta till JavaScript-kod och skapa en ny funktion createTransactionRow:

function createTransactionRow(transaction) {
  const template = document.getElementById('transaction');
  const transactionRow = template.content.cloneNode(true);
  const tr = transactionRow.querySelector('tr');
  tr.children[0].textContent = transaction.date;
  tr.children[1].textContent = transaction.object;
  tr.children[2].textContent = transaction.amount.toFixed(2);
  return transactionRow;
}

Denna funktion gör precis vad dess namn antyder: med hjälp av mallen vi skapade tidigare skapar den en ny tabellrad och fyller dess innehåll med transaktionsdata. Vi kommer att använda detta i vår updateDashboard()-funktion för att fylla tabellen:

const transactionsRows = document.createDocumentFragment();
for (const transaction of account.transactions) {
  const transactionRow = createTransactionRow(transaction);
  transactionsRows.appendChild(transactionRow);
}
updateElement('transactions', transactionsRows);

Här använder vi metoden document.createDocumentFragment() som skapar ett nytt DOM-fragment som vi kan arbeta med innan vi slutligen fäster det i vår HTML-tabell.

Det finns fortfarande en sak vi måste göra innan denna kod kan fungera, eftersom vår updateElement()-funktion för närvarande bara stöder textinnehåll. Låt oss ändra dess kod lite:

function updateElement(id, textOrNode) {
  const element = document.getElementById(id);
  element.textContent = ''; // Removes all children
  element.append(textOrNode);
}

Vi använder metoden append() eftersom den tillåter att fästa antingen text eller DOM-noder till ett överordnat element, vilket är perfekt för alla våra användningsfall. Om du försöker använda test-kontot för att logga in, bör du nu se en transaktionslista på instrumentpanelen 🎉.


🚀 Utmaning

Arbeta tillsammans för att få instrumentpanelsidan att se ut som en riktig bankapp. Om du redan har stylat din app, försök använda media queries för att skapa en responsiv design som fungerar bra både på stationära och mobila enheter.

Här är ett exempel på en stylad instrumentpanelsida:

Skärmdump av ett exempelresultat av instrumentpanelen efter styling

Quiz efter föreläsningen

Quiz efter föreläsningen

Uppgift

Refaktorisera och kommentera din kod


Ansvarsfriskrivning:
Detta dokument har översatts med hjälp av AI-översättningstjänsten 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å dess originalsprå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.