|
3 weeks ago | |
---|---|---|
.. | ||
README.md | 3 weeks ago | |
assignment.md | 3 weeks ago |
README.md
Изградња банкарске апликације, део 3: Методе за преузимање и коришћење података
Квиз пре предавања
Увод
У срцу сваке веб апликације налазе се подаци. Подаци могу имати различите облике, али њихова главна сврха је увек приказивање информација кориснику. Са све интерактивнијим и сложенијим веб апликацијама, начин на који корисник приступа и интерагује са информацијама постао је кључни део веб развоја.
У овој лекцији ћемо видети како да асинхроно преузмемо податке са сервера и користимо их за приказивање информација на веб страници без поновног учитавања HTML-а.
Предуслови
Потребно је да сте изградили формулар за пријаву и регистрацију као део веб апликације за ову лекцију. Такође, потребно је да инсталирате Node.js и покренете серверски API локално како бисте добили податке о налогу.
Можете тестирати да ли сервер ради исправно извршавањем ове команде у терминалу:
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
AJAX и преузимање података
Традиционалне веб странице ажурирају приказани садржај када корисник изабере линк или пошаље податке путем формулара, поновним учитавањем целе HTML странице. Сваки пут када је потребно учитати нове податке, веб сервер враћа потпуно нову HTML страницу коју прегледач мора обрадити, прекидајући тренутну акцију корисника и ограничавајући интеракције током учитавања. Овај начин рада се назива вишестранична апликација или MPA.
Када су веб апликације постале сложеније и интерактивније, појавила се нова техника звана AJAX (асинхрони JavaScript и XML). Ова техника омогућава веб апликацијама да шаљу и преузимају податке са сервера асинхроно користећи JavaScript, без потребе за поновним учитавањем HTML странице, што резултира бржим ажурирањима и глаткијим интеракцијама корисника. Када се нови подаци добију са сервера, тренутна HTML страница може се ажурирати JavaScript-ом користећи DOM API. Временом, овај приступ је еволуирао у оно што се данас назива једностранична апликација или SPA.
Када је AJAX први пут уведен, једини API доступан за асинхроно преузимање података био је XMLHttpRequest
. Међутим, модерни прегледачи сада имплементирају згоднији и моћнији Fetch
API, који користи промисе и боље је прилагођен за манипулацију JSON подацима.
Иако сви модерни прегледачи подржавају
Fetch API
, ако желите да ваша веб апликација ради на старијим прегледачима, увек је добра идеја да прво проверите табелу компатибилности на caniuse.com.
Задатак
У претходној лекцији имплементирали смо формулар за регистрацију како бисмо креирали налог. Сада ћемо додати код за пријаву користећи постојећи налог и преузимање његових података. Отворите датотеку app.js
и додајте нову функцију login
:
async function login() {
const loginForm = document.getElementById('loginForm')
const user = loginForm.user.value;
}
Овде почињемо тако што преузимамо елемент формулара помоћу getElementById()
, а затим добијамо корисничко име из уноса помоћу loginForm.user.value
. Свака контрола формулара може се приступити преко њеног имена (постављеног у HTML-у помоћу атрибута name
) као својству формулара.
Слично ономе што смо урадили за регистрацију, креираћемо још једну функцију за извршавање захтева серверу, али овог пута за преузимање података о налогу:
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' };
}
}
Користимо fetch
API за асинхроно преузимање података са сервера, али овог пута нам нису потребни додатни параметри осим URL-а који позивамо, јер само упитујемо податке. Подразумевано, fetch
креира GET
HTTP захтев, што је управо оно што нам овде треба.
✅ encodeURIComponent()
је функција која ескепује специјалне карактере за URL. Које проблеме бисмо могли имати ако не позовемо ову функцију и директно користимо вредност user
у URL-у?
Сада ажурирамо нашу функцију login
да користи 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');
}
Прво, пошто је getAccount
асинхрона функција, морамо је упарити са кључном речју await
како бисмо сачекали резултат сервера. Као и код сваког захтева серверу, морамо се бавити и случајевима грешке. За сада ћемо само додати поруку у лог за приказивање грешке и вратити се на то касније.
Затим морамо сачувати податке негде како бисмо их касније користили за приказивање информација на контролној табли. Пошто променљива account
још увек не постоји, креираћемо глобалну променљиву за њу на врху наше датотеке:
let account = null;
Након што се кориснички подаци сачувају у променљивој, можемо прећи са странице login на dashboard користећи функцију navigate()
коју већ имамо.
На крају, потребно је да позовемо нашу функцију login
када се формулар за пријаву пошаље, модификујући HTML:
<form id="loginForm" action="javascript:login()">
Тестирајте да ли све ради исправно тако што ћете регистровати нови налог и покушати да се пријавите користећи исти налог.
Пре него што пређемо на следећи део, можемо такође завршити функцију register
додавањем овога на крај функције:
account = result;
navigate('/dashboard');
✅ Да ли сте знали да подразумевано можете позивати серверске API-је само са истог домена и порта као и веб страница коју гледате? Ово је безбедносни механизам који спроводе прегледачи. Али чекајте, наша веб апликација ради на localhost:3000
, док серверски API ради на localhost:5000
, зашто онда ради? Коришћењем технике зване Cross-Origin Resource Sharing (CORS), могуће је извршити HTTP захтеве преко различитих домена ако сервер дода посебне хедере у одговор, дозвољавајући изузетке за одређене домене.
Сазнајте више о API-јима кроз ову лекцију
Ажурирање HTML-а за приказивање података
Сада када имамо корисничке податке, морамо ажурирати постојећи HTML како бисмо их приказали. Већ знамо како да преузмемо елемент из DOM-а, на пример помоћу document.getElementById()
. Након што имате основни елемент, ево неких API-ја које можете користити за његову модификацију или додавање поделемената:
-
Коришћењем својства
textContent
можете променити текст елемента. Имајте на уму да промена ове вредности уклања сву децу елемента (ако их има) и замењује их датим текстом. Као таква, ово је такође ефикасан метод за уклањање свих деце датог елемента додељивањем празног стринга''
. -
Коришћењем
document.createElement()
заједно са методомappend()
можете креирати и додати један или више нових поделемената.
✅ Коришћењем својства innerHTML
елемента могуће је променити његов HTML садржај, али ово би требало избегавати јер је подложно нападима cross-site scripting (XSS).
Задатак
Пре него што пређемо на екран контролне табле, постоји још једна ствар коју треба да урадимо на страници login. Тренутно, ако покушате да се пријавите са корисничким именом које не постоји, порука се приказује у конзоли, али за обичног корисника ништа се не мења и не знате шта се дешава.
Додајмо елемент за приказивање поруке о грешци у формулару за пријаву. Добро место би било непосредно пре дугмета за пријаву <button>
:
...
<div id="loginError"></div>
<button>Login</button>
...
Овај <div>
елемент је празан, што значи да се ништа неће приказати на екрану док не додамо неки садржај. Такође му дајемо id
како бисмо га лакше пронашли помоћу JavaScript-а.
Вратите се у датотеку app.js
и креирајте нову помоћну функцију updateElement
:
function updateElement(id, text) {
const element = document.getElementById(id);
element.textContent = text;
}
Ова функција је прилично једноставна: с обзиром на id елемента и текст, ажурираће текстуални садржај DOM елемента са одговарајућим id
. Користимо ову методу уместо претходне поруке о грешци у функцији login
:
if (data.error) {
return updateElement('loginError', data.error);
}
Сада, ако покушате да се пријавите са неважећим налогом, требало би да видите нешто овако:
Сада имамо текст грешке који се визуелно приказује, али ако га испробате са читачем екрана, приметићете да се ништа не најављује. Да би текст који се динамички додаје на страницу био најављен од стране читача екрана, потребно је да користи нешто што се зове Live Region. Овде ћемо користити специфичан тип live региона зван alert:
<div id="loginError" role="alert"></div>
Примените исто понашање за грешке у функцији register
(не заборавите да ажурирате HTML).
Приказивање информација на контролној табли
Користећи исте технике које смо управо видели, такође ћемо се побринути за приказивање информација о налогу на страници контролне табле.
Овако изгледа објекат налога добијен са сервера:
{
"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 }
],
}
Напомена: да бисте себи олакшали живот, можете користити унапред постојећи налог
test
који је већ попуњен подацима.
Задатак
Почнимо заменом секције "Balance" у HTML-у како бисмо додали елементе за приказивање:
<section>
Balance: <span id="balance"></span><span id="currency"></span>
</section>
Такође ћемо додати нову секцију одмах испод за приказивање описа налога:
<h2 id="description"></h2>
✅ Пошто опис налога функционише као наслов за садржај испод њега, означен је семантички као наслов. Сазнајте више о томе колико је структура наслова важна за приступачност и критички погледајте страницу како бисте утврдили шта још може бити наслов.
Затим ћемо креирати нову функцију у app.js
за попуњавање ових елемената:
function updateDashboard() {
if (!account) {
return navigate('/login');
}
updateElement('description', account.description);
updateElement('balance', account.balance.toFixed(2));
updateElement('currency', account.currency);
}
Прво, проверавамо да ли имамо податке о налогу који су нам потребни пре него што наставимо. Затим користимо функцију updateElement()
коју смо раније креирали за ажурирање HTML-а.
Да би приказивање стања било лепше, користимо методу
toFixed(2)
како бисмо приказали вредност са 2 децимале.
Сада морамо позвати нашу функцију updateDashboard()
сваки пут када се прикаже страница контролне табле. Ако сте већ завршили задатак из лекције 1, ово би требало да буде једноставно, у супротном можете користити следећу имплементацију.
Додајте овај код на крај функције updateRoute()
:
if (typeof route.init === 'function') {
route.init();
}
И ажурирајте дефиницију рута са:
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard', init: updateDashboard }
};
Са овом променом, сваки пут када се прикаже страница контролне табле, позива се функција updateDashboard()
. Након пријаве, требало би да видите стање налога, валуту и опис.
Динамичко креирање редова табеле помоћу HTML шаблона
У првој лекцији користили смо HTML шаблоне заједно са методом appendChild()
за имплементацију навигације у нашој апликацији. Шаблони могу бити и мањи и користити се за динамичко попуњавање понављајућих делова странице.
Користићемо сличан приступ за приказивање листе трансакција у HTML табели.
Задатак
Додајте нови шаблон у <body>
HTML-а:
<template id="transaction">
<tr>
<td></td>
<td></td>
<td></td>
</tr>
</template>
Овај шаблон представља један ред табеле, са 3 колоне које желимо да попунимо: датум, објекат и износ трансакције.
Затим додајте ово својство id
елементу <tbody>
табеле унутар шаблона контролне табле како би га било лакше пронаћи помоћу JavaScript-а:
<tbody id="transactions"></tbody>
Наш HTML је спреман, пређимо на JavaScript код и креирајмо нову функцију 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;
}
Ова функција ради управо оно што њено име имплицира: користећи шаблон који смо раније креирали, креира нови ред табеле и попуњава његов садржај користећи податке о трансакцији. Користићемо ово у нашој функцији updateDashboard()
за попуњавање табеле:
const transactionsRows = document.createDocumentFragment();
for (const transaction of account.transactions) {
const transactionRow = createTransactionRow(transaction);
transactionsRows.appendChild(transactionRow);
}
updateElement('transactions', transactionsRows);
Овде користимо методу document.createDocumentFragment()
која креира нови DOM фрагмент на коме можемо радити, пре него што га коначно додамо у нашу HTML табелу.
Још увек постоји једна ствар коју морамо у
Ако покушате да се пријавите користећи налог test
, сада би требало да видите листу трансакција на контролној табли 🎉.
🚀 Изазов
Радите заједно како бисте страницу контролне табле учинили да изгледа као права апликација за банкарство. Ако сте већ стилизовали своју апликацију, покушајте да користите media queries за креирање респонзивног дизајна који добро функционише и на десктоп и на мобилним уређајима.
Ево примера стилизоване странице контролне табле:
Квиз након предавања
Задатак
Рефакторишите и коментаришите свој код
Одрицање од одговорности:
Овај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције Co-op Translator. Иако се трудимо да обезбедимо тачност, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на његовом изворном језику треба сматрати меродавним извором. За критичне информације препоручује се професионални превод од стране људи. Не сносимо одговорност за било каква погрешна тумачења или неспоразуме који могу настати услед коришћења овог превода.