18 KiB
Rakenna pankkisovellus Osa 3: Datan hakeminen ja käyttäminen
Ennakkokysely
Johdanto
Jokaisen verkkosovelluksen ytimessä on data. Data voi esiintyä monessa muodossa, mutta sen pääasiallinen tarkoitus on aina näyttää tietoa käyttäjälle. Verkkosovellusten tullessa yhä vuorovaikutteisemmiksi ja monimutkaisemmiksi, siitä, miten käyttäjä pääsee käsiksi tietoon ja käyttää sitä, on tullut keskeinen osa verkkokehitystä.
Tässä oppitunnissa opimme, miten dataa haetaan palvelimelta asynkronisesti ja miten sitä käytetään tietojen näyttämiseen verkkosivulla ilman HTML:n uudelleenlatausta.
Esitiedot
Sinun tulee olla rakentanut kirjautumis- ja rekisteröintilomake osana verkkosovellusta ennen tämän oppitunnin aloittamista. Sinun tulee myös asentaa Node.js ja ajaa palvelin-API paikallisesti, jotta saat tilitiedot.
Voit testata, että palvelin toimii oikein suorittamalla tämän komennon terminaalissa:
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
AJAX ja datan hakeminen
Perinteiset verkkosivustot päivittävät näytettävän sisällön, kun käyttäjä valitsee linkin tai lähettää tietoja lomakkeella, lataamalla koko HTML-sivun uudelleen. Joka kerta, kun uutta dataa täytyy ladata, verkkopalvelin palauttaa täysin uuden HTML-sivun, joka selaimen täytyy käsitellä. Tämä keskeyttää käyttäjän nykyisen toiminnan ja rajoittaa vuorovaikutusta latauksen aikana. Tätä työnkulkua kutsutaan myös nimellä Multi-Page Application eli MPA.
Kun verkkosovellukset alkoivat muuttua monimutkaisemmiksi ja vuorovaikutteisemmiksi, syntyi uusi tekniikka nimeltä AJAX (Asynchronous JavaScript and XML). Tämä tekniikka mahdollistaa datan lähettämisen ja hakemisen palvelimelta asynkronisesti JavaScriptin avulla ilman HTML-sivun uudelleenlatausta, mikä johtaa nopeampiin päivityksiin ja sujuvampaan käyttäjäkokemukseen. Kun palvelimelta saadaan uutta dataa, nykyistä HTML-sivua voidaan päivittää JavaScriptin avulla käyttämällä DOM-rajapintaa. Ajan myötä tämä lähestymistapa on kehittynyt siihen, mitä nykyään kutsutaan Single-Page Application eli SPA.
Kun AJAX esiteltiin ensimmäisen kerran, ainoa käytettävissä oleva API datan asynkroniseen hakemiseen oli XMLHttpRequest
. Nykyään modernit selaimet tukevat myös kätevämpää ja tehokkaampaa Fetch
APIa, joka käyttää promiseja ja soveltuu paremmin JSON-datan käsittelyyn.
Vaikka kaikki modernit selaimet tukevat
Fetch APIa
, jos haluat verkkosovelluksesi toimivan vanhoissa selaimissa, on aina hyvä idea tarkistaa yhteensopivuustaulukko caniuse.com-sivustolta.
Tehtävä
Edellisessä oppitunnissa toteutimme rekisteröintilomakkeen tilin luomista varten. Nyt lisäämme koodia kirjautumiseen olemassa olevaa tiliä käyttäen ja datan hakemiseen. Avaa app.js
-tiedosto ja lisää uusi login
-funktio:
async function login() {
const loginForm = document.getElementById('loginForm')
const user = loginForm.user.value;
}
Aloitamme hakemalla lomake-elementin getElementById()
-metodilla ja saamme käyttäjänimen syötteestä loginForm.user.value
. Jokainen lomakekenttä voidaan hakea sen nimen (asetettu HTML:ssä name
-attribuutilla) avulla lomakkeen ominaisuutena.
Samalla tavalla kuin rekisteröinnissä, luomme toisen funktion palvelinpyyntöä varten, mutta tällä kertaa tilitietojen hakemiseen:
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' };
}
}
Käytämme fetch
APIa datan asynkroniseen hakemiseen palvelimelta, mutta tällä kertaa emme tarvitse muita parametreja kuin kutsuttavan URL-osoitteen, koska haemme vain dataa. Oletuksena fetch
luo GET
-HTTP-pyynnön, mikä on juuri se, mitä tarvitsemme tässä.
✅ encodeURIComponent()
on funktio, joka koodaa erikoismerkit URL-osoitteeseen. Mitä ongelmia voisi ilmetä, jos emme kutsuisi tätä funktiota ja käyttäisimme suoraan user
-arvoa URL-osoitteessa?
Päivitetään nyt login
-funktiomme käyttämään getAccount
-funktiota:
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');
}
Koska getAccount
on asynkroninen funktio, meidän täytyy käyttää await
-avainsanaa odottaaksemme palvelimen vastausta. Kuten kaikissa palvelinpyynnöissä, meidän täytyy myös käsitellä virhetilanteet. Toistaiseksi lisäämme vain lokiviestin virheen näyttämiseksi ja palaamme tähän myöhemmin.
Seuraavaksi meidän täytyy tallentaa data johonkin, jotta voimme käyttää sitä myöhemmin kojelaudan tietojen näyttämiseen. Koska account
-muuttujaa ei vielä ole, luomme globaalin muuttujan tiedoston alkuun:
let account = null;
Kun käyttäjätiedot on tallennettu muuttujaan, voimme siirtyä kirjautumissivulta kojelaudalle käyttämällä navigate()
-funktiota, joka meillä jo on.
Lopuksi meidän täytyy kutsua login
-funktiota, kun kirjautumislomake lähetetään, muokkaamalla HTML:ää:
<form id="loginForm" action="javascript:login()">
Testaa, että kaikki toimii oikein rekisteröimällä uusi tili ja yrittämällä kirjautua sisään samalla tilillä.
Ennen kuin siirrymme seuraavaan osaan, voimme myös täydentää register
-funktiota lisäämällä tämän funktion loppuun:
account = result;
navigate('/dashboard');
✅ Tiesitkö, että oletuksena voit kutsua palvelin-APIa vain samasta domainista ja portista, josta katsot verkkosivua? Tämä on selainten pakottama tietoturvamekanismi. Mutta hetkinen, verkkosovelluksemme pyörii localhost:3000
-osoitteessa, kun taas palvelin-API pyörii localhost:5000
-osoitteessa. Miksi se toimii? Käyttämällä tekniikkaa nimeltä Cross-Origin Resource Sharing (CORS) on mahdollista suorittaa cross-origin HTTP-pyyntöjä, jos palvelin lisää erityisiä otsikoita vastaukseen, sallien poikkeuksia tietyille domaineille.
Opi lisää APIsta tämän oppitunnin avulla.
Päivitä HTML näyttämään dataa
Nyt kun meillä on käyttäjätiedot, meidän täytyy päivittää olemassa oleva HTML näyttämään ne. Tiedämme jo, miten elementti haetaan DOMista esimerkiksi document.getElementById()
-metodilla. Kun sinulla on peruselementti, tässä on joitakin rajapintoja, joita voit käyttää sen muokkaamiseen tai lapsielementtien lisäämiseen:
-
textContent
-ominaisuutta käyttämällä voit muuttaa elementin tekstiä. Huomaa, että tämän arvon muuttaminen poistaa kaikki elementin lapset (jos niitä on) ja korvaa ne annetulla tekstillä. Näin ollen se on myös tehokas tapa poistaa kaikki annetun elementin lapset asettamalla arvoksi tyhjä merkkijono''
. -
document.createElement()
-metodia yhdessäappend()
-metodin kanssa käyttämällä voit luoda ja liittää yhden tai useamman uuden lapsielementin.
✅ innerHTML
-ominaisuutta käyttämällä on myös mahdollista muuttaa elementin HTML-sisältöä, mutta tätä tulisi välttää, koska se on altis cross-site scripting (XSS)-hyökkäyksille.
Tehtävä
Ennen kuin siirrymme kojelautanäkymään, meidän täytyy tehdä vielä yksi asia kirjautumissivulla. Tällä hetkellä, jos yrität kirjautua käyttäjänimellä, jota ei ole olemassa, viesti näytetään konsolissa, mutta tavalliselle käyttäjälle ei tapahdu mitään, eikä hän tiedä, mitä tapahtuu.
Lisätään paikkamerkki-elementti kirjautumislomakkeeseen, jossa voimme näyttää virheilmoituksen tarvittaessa. Hyvä paikka olisi juuri ennen kirjautumis-<button>
-elementtiä:
...
<div id="loginError"></div>
<button>Login</button>
...
Tämä <div>
-elementti on tyhjä, mikä tarkoittaa, että mitään ei näytetä näytöllä, ennen kuin lisäämme siihen sisältöä. Annamme sille myös id
-tunnisteen, jotta voimme hakea sen helposti JavaScriptillä.
Palaa app.js
-tiedostoon ja luo uusi apufunktio updateElement
:
function updateElement(id, text) {
const element = document.getElementById(id);
element.textContent = text;
}
Tämä on melko suoraviivainen: annettuaan elementin id:n ja tekstin, se päivittää vastaavan DOM-elementin tekstisisällön. Käytetään tätä metodia aiemman virheilmoituksen tilalla login
-funktiossa:
if (data.error) {
return updateElement('loginError', data.error);
}
Nyt, jos yrität kirjautua virheellisellä tilillä, sinun pitäisi nähdä jotain tällaista:
Nyt meillä on visuaalisesti näkyvä virheteksti, mutta jos kokeilet sitä ruudunlukijalla, huomaat, että mitään ei ilmoiteta. Jotta dynaamisesti lisätty teksti sivulle ilmoitettaisiin ruudunlukijoilla, sen täytyy käyttää jotain, jota kutsutaan Live Region-alueeksi. Tässä käytämme erityistä live-aluetta nimeltä alert:
<div id="loginError" role="alert"></div>
Toteuta sama toiminnallisuus myös register
-funktion virheille (älä unohda päivittää HTML:ää).
Näytä tietoja kojelaudalla
Käyttämällä samoja tekniikoita, joita olemme juuri nähneet, huolehdimme myös tilitietojen näyttämisestä kojelautasivulla.
Tältä palvelimelta saatu tilitieto-objekti näyttää:
{
"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 }
],
}
Huom: elämän helpottamiseksi voit käyttää valmiiksi luotua
test
-tiliä, joka on jo täytetty tiedoilla.
Tehtävä
Aloitetaan korvaamalla "Saldo"-osio HTML:ssä lisäämällä paikkamerkki-elementit:
<section>
Balance: <span id="balance"></span><span id="currency"></span>
</section>
Lisätään myös uusi osio heti tämän alapuolelle tilin kuvauksen näyttämistä varten:
<h2 id="description"></h2>
✅ Koska tilin kuvaus toimii otsikkona sen alapuolella olevalle sisällölle, se on merkitty semanttisesti otsikoksi. Opi lisää, miksi otsikkorakenne on tärkeä saavutettavuuden kannalta, ja tarkastele kriittisesti sivua päättääksesi, mitä muuta voisi olla otsikko.
Seuraavaksi luomme uuden funktion app.js
-tiedostoon paikkamerkkien täyttämiseksi:
function updateDashboard() {
if (!account) {
return navigate('/login');
}
updateElement('description', account.description);
updateElement('balance', account.balance.toFixed(2));
updateElement('currency', account.currency);
}
Ensin tarkistamme, että meillä on tarvittavat tilitiedot ennen kuin jatkamme eteenpäin. Sitten käytämme aiemmin luomaamme updateElement()
-funktiota HTML:n päivittämiseen.
Jotta saldon näyttö olisi siistimpi, käytämme metodia
toFixed(2)
pakottaaksemme arvon näyttämään kaksi desimaalia.
Nyt meidän täytyy kutsua updateDashboard()
-funktiota aina, kun kojelautasivu ladataan. Jos olet jo suorittanut oppitunnin 1 tehtävän, tämän pitäisi olla suoraviivaista, muuten voit käyttää seuraavaa toteutusta.
Lisää tämä koodi updateRoute()
-funktion loppuun:
if (typeof route.init === 'function') {
route.init();
}
Ja päivitä reittimäärittelyt seuraavasti:
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard', init: updateDashboard }
};
Tämän muutoksen myötä aina, kun kojelautasivu näytetään, updateDashboard()
-funktio kutsutaan. Kirjautumisen jälkeen sinun pitäisi nähdä tilin saldo, valuutta ja kuvaus.
Luo taulukkorivejä dynaamisesti HTML-mallien avulla
Ensimmäisessä oppitunnissa käytimme HTML-malleja yhdessä appendChild()
-metodin kanssa toteuttaaksemme sovelluksen navigoinnin. Mallit voivat olla myös pienempiä ja niitä voidaan käyttää dynaamisesti toistuvien osien täyttämiseen sivulla.
Käytämme samanlaista lähestymistapaa näyttääksemme tapahtumaluettelon HTML-taulukossa.
Tehtävä
Lisää uusi malli HTML-<body>
-osioon:
<template id="transaction">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</template>
Tämä malli edustaa yksittäistä taulukkoriviä, jossa on kolme saraketta: tapahtuman päivämäärä, kohde ja summa.
Lisää sitten tämä id
-ominaisuus taulukon <tbody>
-elementtiin kojelautamallin sisällä, jotta se on helpompi löytää JavaScriptillä:
<tbody id="transactions"></tbody>
HTML on valmis, siirrytään JavaScript-koodiin ja luodaan uusi funktio 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;
}
Tämä funktio tekee juuri sen, mitä sen nimi viittaa: käyttämällä aiemmin luomaamme mallia se luo uuden taulukkorivin ja täyttää sen sisällön tapahtumatiedoilla. Käytämme tätä updateDashboard()
-funktiossamme taulukon täyttämiseen:
const transactionsRows = document.createDocumentFragment();
for (const transaction of account.transactions) {
const transactionRow = createTransactionRow(transaction);
transactionsRows.appendChild(transactionRow);
}
updateElement('transactions', transactionsRows);
Tässä käytämme metodia document.createDocumentFragment()
, joka luo uuden DOM-fragmentin, johon voimme tehdä muutoksia ennen sen liittämistä HTML-taulukkoon.
On vielä yksi asia, joka meidän täytyy tehdä ennen kuin tämä koodi toimii, sillä updateElement()
-funktiomme tukee tällä hetkellä vain tekstisisältöä. Muutetaan sen koodia hieman:
function updateElement(id, textOrNode) {
const element = document.getElementById(id);
element.textContent = ''; // Removes all children
element.append(textOrNode);
}
Käytämme append()
-metodia, koska sen avulla voimme liittää joko tekstiä tai DOM Nodeja vanhempaan elementtiin, mikä sopii täydellisesti kaikkiin käyttötapauksiimme.
Jos yrität kirjautua sisään käyttämällä test
-tiliä, sinun pitäisi nyt nähdä tapahtumaluettelo hallintapaneelissa 🎉.
🚀 Haaste
Työskennelkää yhdessä saadaksenne hallintapaneelisivun näyttämään oikealta pankkisovellukselta. Jos olet jo muotoillut sovelluksesi, yritä käyttää media queries -ominaisuutta luodaksesi responsiivisen suunnittelun, joka toimii hyvin sekä työpöytä- että mobiililaitteilla.
Tässä esimerkki muotoillusta hallintapaneelisivusta:
Luentojälkeinen visailu
Tehtävä
Refaktoroi ja kommentoi koodisi
Vastuuvapauslauseke:
Tämä asiakirja on käännetty käyttämällä tekoälypohjaista käännöspalvelua Co-op Translator. Vaikka pyrimme tarkkuuteen, huomioithan, että automaattiset käännökset voivat sisältää virheitä tai epätarkkuuksia. Alkuperäinen asiakirja sen alkuperäisellä kielellä tulisi pitää ensisijaisena lähteenä. Kriittisen tiedon osalta suositellaan ammattimaista ihmiskäännöstä. Emme ole vastuussa väärinkäsityksistä tai virhetulkinnoista, jotka johtuvat tämän käännöksen käytöstä.