|
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 садржај, али ово треба избегавати јер је рањиво на нападе преко скриптовања сајта (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 табели.
Задатак
Додајте нови шаблон у HTML <body>
:
<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
, сада би требало да видите листу трансакција на контролној табли 🎉.
🚀 Изазов
Радите заједно како бисте страницу контролне табле учинили да изгледа као права апликација за банкарство. Ако сте већ стилизовали своју апликацију, покушајте да користите медијске упите како бисте креирали респонзиван дизајн који добро функционише и на десктоп и на мобилним уређајима.
Ево примера стилизоване странице контролне табле:
Квиз након предавања
Задатак
Рефакторишите и коментаришите свој код
Одрицање од одговорности:
Овај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције Co-op Translator. Иако настојимо да обезбедимо тачност, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на изворном језику треба сматрати меродавним извором. За критичне информације препоручује се професионални превод од стране људи. Не сносимо одговорност за било каква погрешна тумачења или неспоразуме који могу произаћи из коришћења овог превода.