# Rakenna pankkisovellus Osa 3: Datan hakeminen ja käyttäminen ## Ennakkokysely [Ennakkokysely](https://ff-quizzes.netlify.app/web/quiz/45) ### 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](../2-forms/README.md) osana verkkosovellusta ennen tämän oppitunnin aloittamista. Sinun tulee myös asentaa [Node.js](https://nodejs.org) ja [ajaa palvelin-API](../api/README.md) paikallisesti, jotta saat tilitiedot. Voit testata, että palvelin toimii oikein suorittamalla tämän komennon terminaalissa: ```sh 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)](https://en.wikipedia.org/wiki/Ajax_(programming)). 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](https://developer.mozilla.org/docs/Web/API/Document_Object_Model)-rajapintaa. Ajan myötä tämä lähestymistapa on kehittynyt siihen, mitä nykyään kutsutaan [*Single-Page Application* eli *SPA*](https://en.wikipedia.org/wiki/Single-page_application).  Kun AJAX esiteltiin ensimmäisen kerran, ainoa käytettävissä oleva API datan asynkroniseen hakemiseen oli [`XMLHttpRequest`](https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest). Nykyään modernit selaimet tukevat myös kätevämpää ja tehokkaampaa [`Fetch` APIa](https://developer.mozilla.org/docs/Web/API/Fetch_API), 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](https://caniuse.com/fetch). ### Tehtävä [Edellisessä oppitunnissa](../2-forms/README.md) 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: ```js 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: ```js 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`](https://developer.mozilla.org/docs/Web/HTTP/Methods/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: ```js 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: ```js 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:ää: ```html