27 KiB
Изградња банкарске апликације, део 2: Израда форме за пријаву и регистрацију
Квиз пре предавања
Увод
У готово свим модерним веб апликацијама можете креирати налог како бисте имали свој приватни простор. Како више корисника може истовремено приступити веб апликацији, потребан је механизам за одвојено чување личних података сваког корисника и одабир информација које ће бити приказане. Нећемо се бавити како сигурно управљати идентитетом корисника, јер је то обимна тема сама по себи, али ћемо осигурати да сваки корисник може креирати један (или више) банковних рачуна у нашој апликацији.
У овом делу ћемо користити HTML форме за додавање пријаве и регистрације у нашу веб апликацију. Видећемо како програмски послати податке серверској API-ју и на крају како дефинисати основна правила за валидацију корисничких уноса.
Предуслови
Потребно је да сте завршили HTML шаблоне и рутирање веб апликације за ову лекцију. Такође је потребно да инсталирате Node.js и покренете серверски API локално како бисте могли слати податке за креирање налога.
Обратите пажњу
Имаћете два терминала која раде истовремено, како је наведено у наставку:
- За главну банкарску апликацију коју смо изградили у лекцији HTML шаблони и рутирање
- За серверски API банкарске апликације који смо управо подесили.
Потребно је да оба сервера буду покренута како бисте могли пратити остатак лекције. Они слушају на различитим портовима (порт 3000
и порт 5000
), тако да би све требало да функционише без проблема.
Можете тестирати да ли сервер ради исправно извршавањем ове команде у терминалу:
curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result
Форма и контроле
Елемент <form>
обухвата део HTML документа где корисник може уносити и слати податке помоћу интерактивних контрола. Постоје разне корисничке интерфејс (UI) контроле које се могу користити унутар форме, а најчешће су <input>
и <button>
елементи.
Постоји много различитих типова <input>
елемената. На пример, за креирање поља где корисник може унети своје корисничко име можете користити:
<input id="username" name="username" type="text">
Атрибут name
ће се користити као назив својства када се подаци форме шаљу. Атрибут id
се користи за повезивање <label>
елемента са контролом форме.
Погледајте целу листу
<input>
типова и друге контроле форме како бисте стекли идеју о свим нативним UI елементима које можете користити приликом изградње вашег интерфејса.
✅ Имајте на уму да је <input>
празан елемент коме не треба додавати одговарајући завршни таг. Можете, међутим, користити самозатварајућу нотацију <input/>
, али то није обавезно.
Елемент <button>
унутар форме је мало посебан. Ако не наведете његов атрибут type
, аутоматски ће послати податке форме серверу када се притисне. Ево могућих вредности за type
:
submit
: Подразумевано унутар<form>
, дугме покреће акцију слања форме.reset
: Дугме ресетује све контроле форме на њихове почетне вредности.button
: Не додељује подразумевано понашање када се дугме притисне. Можете му доделити прилагођене акције помоћу JavaScript-а.
Задатак
Почнимо са додавањем форме у login
шаблон. Биће нам потребно поље за корисничко име и дугме за Пријаву.
<template id="login">
<h1>Bank App</h1>
<section>
<h2>Login</h2>
<form id="loginForm">
<label for="username">Username</label>
<input id="username" name="user" type="text">
<button>Login</button>
</form>
</section>
</template>
Ако пажљивије погледате, приметићете да смо овде додали и <label>
елемент. <label>
елементи се користе за додавање назива UI контролама, као што је наше поље за корисничко име. Ознаке су важне за читљивост ваших форми, али такође долазе са додатним предностима:
- Повезивањем ознаке са контролом форме, помаже корисницима који користе асистивне технологије (попут читача екрана) да разумеју које податке треба да унесу.
- Можете кликнути на ознаку да бисте директно ставили фокус на повезани унос, што олакшава коришћење на уређајима са екраном осетљивим на додир.
Приступачност на вебу је веома важна тема која се често занемарује. Захваљујући семантичким HTML елементима није тешко креирати приступачан садржај ако их правилно користите. Можете прочитати више о приступачности како бисте избегли уобичајене грешке и постали одговоран програмер.
Сада ћемо додати другу форму за регистрацију, одмах испод претходне:
<hr/>
<h2>Register</h2>
<form id="registerForm">
<label for="user">Username</label>
<input id="user" name="user" type="text">
<label for="currency">Currency</label>
<input id="currency" name="currency" type="text" value="$">
<label for="description">Description</label>
<input id="description" name="description" type="text">
<label for="balance">Current balance</label>
<input id="balance" name="balance" type="number" value="0">
<button>Register</button>
</form>
Коришћењем атрибута value
можемо дефинисати подразумевану вредност за дати унос.
Такође приметите да унос за balance
има тип number
. Да ли изгледа другачије од осталих уноса? Пробајте да интерагујете са њим.
✅ Можете ли навигирати и интераговати са формама користећи само тастатуру? Како бисте то урадили?
Слање података серверу
Сада када имамо функционалан UI, следећи корак је слање података серверу. Хајде да направимо брз тест користећи наш тренутни код: шта се дешава ако кликнете на дугме Login или Register?
Да ли сте приметили промену у URL секцији вашег претраживача?
Подразумевана акција за <form>
је да пошаље форму тренутном серверском URL-у користећи GET метод, додајући податке форме директно у URL. Овај метод има неке недостатке:
- Подаци који се шаљу су веома ограничени по величини (око 2000 карактера)
- Подаци су директно видљиви у URL-у (што није добро за лозинке)
- Не ради са отпремањем датотека
Зато можете променити метод на POST метод који шаље податке форме серверу у телу HTTP захтева, без претходно наведених ограничења.
Иако је POST најчешће коришћен метод за слање података, у неким специфичним сценаријима је пожељно користити GET метод, на пример приликом имплементације поља за претрагу.
Задатак
Додајте action
и method
својства форми за регистрацију:
<form id="registerForm" action="//localhost:5000/api/accounts" method="POST">
Сада покушајте да региструјете нови налог са вашим именом. Након клика на дугме Register требало би да видите нешто овако:
Ако све функционише како треба, сервер би требало да одговори на ваш захтев са JSON одговором који садржи податке о креираном налогу.
✅ Покушајте поново да се региструјете са истим именом. Шта се дешава?
Слање података без освежавања странице
Као што сте вероватно приметили, постоји мали проблем са приступом који смо управо користили: приликом слања форме, излазимо из наше апликације и претраживач се преусмерава на серверски URL. Трудимо се да избегнемо сва освежавања странице у нашој веб апликацији, јер правимо једностраницу апликацију (SPA).
Да бисмо послали податке форме серверу без приморавања на освежавање странице, морамо користити JavaScript код. Уместо да ставимо URL у action
својство <form>
елемента, можемо користити било који JavaScript код са префиксом javascript:
за извршавање прилагођене акције. Коришћење овога такође значи да ћете морати имплементирати неке задатке који су раније аутоматски обављани од стране претраживача:
- Преузимање података форме
- Конвертовање и кодирање података форме у одговарајући формат
- Креирање HTTP захтева и слање серверу
Задатак
Замените action
форме за регистрацију са:
<form id="registerForm" action="javascript:register()">
Отворите app.js
и додајте нову функцију под именом register
:
function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const data = Object.fromEntries(formData);
const jsonData = JSON.stringify(data);
}
Овде преузимамо елемент форме користећи getElementById()
и користимо FormData
помоћни алат за екстракцију вредности из контрола форме као скуп парова кључ/вредност. Затим конвертујемо податке у регуларни објекат користећи Object.fromEntries()
и на крају серијализујемо податке у JSON, формат који се често користи за размену података на вебу.
Подаци су сада спремни за слање серверу. Креирајте нову функцију под именом createAccount
:
async function createAccount(account) {
try {
const response = await fetch('//localhost:5000/api/accounts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: account
});
return await response.json();
} catch (error) {
return { error: error.message || 'Unknown error' };
}
}
Шта ради ова функција? Прво, приметите async
кључну реч овде. То значи да функција садржи код који ће се извршавати асинхроно. Када се користи заједно са await
кључном речи, омогућава чекање на извршење асинхроног кода - као што је чекање на одговор сервера овде - пре наставка.
Ево кратког видеа о коришћењу async/await
:
🎥 Кликните на слику изнад за видео о async/await.
Користимо fetch()
API за слање JSON података серверу. Овај метод узима 2 параметра:
- URL сервера, тако да овде враћамо
//localhost:5000/api/accounts
. - Подешавања захтева. Ту постављамо метод на
POST
и обезбеђујемоbody
за захтев. Како шаљемо JSON податке серверу, такође морамо поставитиContent-Type
хедер наapplication/json
како би сервер знао како да интерпретира садржај.
Како ће сервер одговорити на захтев са JSON-ом, можемо користити await response.json()
за парсирање JSON садржаја и враћање резултујућег објекта. Имајте на уму да је овај метод асинхрон, па користимо await
кључну реч овде пре враћања како бисмо били сигурни да су све грешке током парсирања такође ухваћене.
Сада додајте мало кода у функцију register
како бисте позвали createAccount()
:
const result = await createAccount(jsonData);
Како користимо await
кључну реч овде, потребно је додати async
кључну реч пре функције register:
async function register() {
На крају, додајмо неке логове како бисмо проверили резултат. Коначна функција би требало да изгледа овако:
async function register() {
const registerForm = document.getElementById('registerForm');
const formData = new FormData(registerForm);
const jsonData = JSON.stringify(Object.fromEntries(formData));
const result = await createAccount(jsonData);
if (result.error) {
return console.log('An error occurred:', result.error);
}
console.log('Account created!', result);
}
То је било мало дуго, али смо успели! Ако отворите алатке за програмере претраживача и покушате да региструјете нови налог, не би требало да видите никакву промену на веб страници, али ће се у конзоли појавити порука која потврђује да све функционише.
✅ Да ли мислите да се подаци шаљу серверу безбедно? Шта ако неко успе да пресретне захтев? Можете прочитати о HTTPS како бисте сазнали више о сигурној комуникацији података.
Валидација података
Ако покушате да региструјете нови налог без претходног уноса корисничког имена, можете видети да сервер враћа грешку са статусним кодом 400 (Bad Request).
Пре слања података серверу добра је пракса валидирати податке форме унапред када је то могуће, како бисте били сигурни да шаљете важећи захтев. HTML5 контроле форме пружају уграђену валидацију користећи различите атрибуте:
required
: поље мора бити попуњено, иначе форма не може бити послата.minlength
иmaxlength
: дефинишу минималан и максималан број карактера у текстуалним пољима.min
иmax
: дефинишу минималну и максималну вредност нумеричког поља.type
: дефинише врсту очекиваних података, као што суnumber
,email
,file
или други уграђени типови. Овај атрибут такође може променити визуелни приказ контроле форме.pattern
: омогућава дефинисање регуларног израза за проверу да ли су унети подаци валидни или не.
Савет: можете прилагодити изглед контролних елемената вашег обрасца у зависности од тога да ли су валидни или не, користећи CSS псеудо-класе
:valid
и:invalid
.
Задатак
Постоје два обавезна поља за креирање важећег новог налога: корисничко име и валута, док су остала поља опционална. Ажурирајте HTML форме, користећи и required
атрибут и текст у називу поља тако да:
<label for="user">Username (required)</label>
<input id="user" name="user" type="text" required>
...
<label for="currency">Currency (required)</label>
<input id="currency" name="currency" type="text" value="$" required>
Иако ова конкретна серверска имплементација не намеће специфична ограничења за максималну дужину поља, увек је добра пракса да се дефинишу разумна ограничења за било који унос текста од стране корисника.
Додајте maxlength
атрибут текстуалним пољима:
<input id="user" name="user" type="text" maxlength="20" required>
...
<input id="currency" name="currency" type="text" value="$" maxlength="5" required>
...
<input id="description" name="description" type="text" maxlength="100">
Сада, ако притиснете дугме Региструј се и неко поље не поштује правило валидације које смо дефинисали, требало би да видите нешто овако:
Валидација која се обавља пре слања било каквих података серверу назива се валидација на страни клијента. Али имајте на уму да није увек могуће обавити све провере без слања података. На пример, овде не можемо проверити да ли већ постоји налог са истим корисничким именом без слања захтева серверу. Додатна валидација која се обавља на серверу назива се валидација на страни сервера.
Обично је потребно имплементирати обе врсте валидације, и док валидација на страни клијента побољшава корисничко искуство пружањем тренутних повратних информација кориснику, валидација на страни сервера је кључна за осигурање да су подаци корисника које обрађујете исправни и безбедни.
🚀 Изазов
Прикажите поруку о грешци у HTML-у ако корисник већ постоји.
Ево примера како може изгледати коначна страница за пријаву након мало стилизовања:
Квиз након предавања
Преглед и самостално учење
Програмери су постали веома креативни у својим напорима за креирање форми, посебно када је реч о стратегијама валидације. Сазнајте више о различитим токовима форми прегледајући CodePen; можете ли пронаћи неке занимљиве и инспиративне форме?
Задатак
Стилизујте своју апликацију за банкарство
Одрицање од одговорности:
Овај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције Co-op Translator. Иако настојимо да обезбедимо тачност, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на изворном језику треба сматрати ауторитативним извором. За критичне информације препоручује се професионални превод од стране људи. Не сносимо одговорност за било каква неспоразумевања или погрешна тумачења која могу произаћи из коришћења овог превода.