# Изградња банкарске апликације, део 2: Изградња формулара за пријаву и регистрацију ## Квиз пре предавања [Квиз пре предавања](https://ff-quizzes.netlify.app/web/quiz/43) ### Увод У скоро свим модерним веб апликацијама можете креирати налог како бисте имали свој приватни простор. Пошто више корисника може истовремено приступити веб апликацији, потребан је механизам за одвојено чување личних података сваког корисника и избор информација које ће бити приказане. Нећемо покривати како [безбедно управљати идентитетом корисника](https://en.wikipedia.org/wiki/Authentication), јер је то обимна тема сама по себи, али ћемо осигурати да сваки корисник може креирати један (или више) банковних рачуна у нашој апликацији. У овом делу ћемо користити HTML формуларе за додавање пријаве и регистрације у нашу веб апликацију. Видећемо како програмски послати податке серверској API-ју и на крају како дефинисати основна правила за валидацију корисничких уноса. ### Предуслови Потребно је да сте завршили [HTML шаблоне и рутирање](../1-template-route/README.md) веб апликације за ову лекцију. Такође је потребно да инсталирате [Node.js](https://nodejs.org) и [покренете серверску API](../api/README.md) локално како бисте могли да шаљете податке за креирање налога. **Обратите пажњу** Имаћете два терминала која раде истовремено, као што је наведено у наставку: 1. За главну банкарску апликацију коју смо изградили у лекцији [HTML шаблони и рутирање](../1-template-route/README.md) 2. За [серверску API банкарске апликације](../api/README.md) коју смо управо подесили. Потребно је да оба сервера буду покренута како бисте могли да наставите са остатком лекције. Они слушају на различитим портовима (порт `3000` и порт `5000`), тако да би све требало да функционише без проблема. Можете тестирати да ли сервер ради исправно извршавањем ове команде у терминалу: ```sh curl http://localhost:5000/api # -> should return "Bank API v1.0.0" as a result ``` --- ## Формулар и контроле Елемент `
` обухвата део HTML документа где корисник може уносити и слати податке помоћу интерактивних контрола. Постоји много различитих корисничких интерфејс (UI) контрола које се могу користити унутар формулара, а најчешћи су елементи `` и `
``` Користећи атрибут `value` можемо дефинисати подразумевану вредност за дати унос. Такође приметите да унос за `balance` има тип `number`. Да ли изгледа другачије од осталих уноса? Покушајте да интерагујете са њим. ✅ Можете ли навигирати и интераговати са формуларима користећи само тастатуру? Како бисте то урадили? ## Слање података серверу Сада када имамо функционалан UI, следећи корак је слање података серверу. Хајде да направимо брз тест користећи наш тренутни код: шта се дешава ако кликнете на дугме *Login* или *Register*? Да ли сте приметили промену у URL секцији вашег претраживача? ![Снимак екрана са променом URL-а претраживача након клика на дугме Register](../../../../translated_images/click-register.e89a30bf0d4bc9ca867dc537c4cea679a7c26368bd790969082f524fed2355bc.sr.png) Подразумевана акција за `
` је да пошаље формулар на тренутни URL сервера користећи [GET метод](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.3), додајући податке формулара директно у URL. Овај метод има неке недостатке: - Подаци који се шаљу су веома ограничени по величини (око 2000 карактера) - Подаци су директно видљиви у URL-у (није добро за лозинке) - Не ради са отпремањем датотека Зато можете променити да користите [POST метод](https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5) који шаље податке формулара серверу у телу HTTP захтева, без претходних ограничења. > Иако је POST најчешће коришћен метод за слање података, [у неким специфичним сценаријима](https://www.w3.org/2001/tag/doc/whenToUseGet.html) је пожељно користити GET метод, на пример приликом имплементације поља за претрагу. ### Задатак Додајте `action` и `method` својства формулару за регистрацију: ```html ``` Сада покушајте да региструјете нови налог са вашим именом. Након клика на дугме *Register* требало би да видите нешто овако: ![Прозор претраживача на адреси localhost:5000/api/accounts, приказује JSON стринг са корисничким подацима](../../../../translated_images/form-post.61de4ca1b964d91a9e338416e19f218504dd0af5f762fbebabfe7ae80edf885f.sr.png) Ако све функционише како треба, сервер би требало да одговори на ваш захтев са [JSON](https://www.json.org/json-en.html) одговором који садржи податке о креираном налогу. ✅ Покушајте поново да се региструјете са истим именом. Шта се дешава? ## Слање података без поновног учитавања странице Као што сте вероватно приметили, постоји мали проблем са приступом који смо управо користили: приликом слања формулара, излазимо из наше апликације и претраживач се преусмерава на URL сервера. Покушавамо да избегнемо сва поновна учитавања страница у нашој веб апликацији, јер правимо [апликацију са једном страницом (SPA)](https://en.wikipedia.org/wiki/Single-page_application). Да бисмо послали податке формулара серверу без принудног поновног учитавања странице, морамо користити JavaScript код. Уместо да ставимо URL у својство `action` елемента ``, можете користити било који JavaScript код са префиксом `javascript:` да бисте извршили прилагођену акцију. Коришћење овога такође значи да ћете морати да имплементирате неке задатке који су раније аутоматски обављани од стране претраживача: - Преузимање података формулара - Конвертовање и кодирање података формулара у одговарајући формат - Креирање HTTP захтева и његово слање серверу ### Задатак Замените `action` формулара за регистрацију са: ```html ``` Отворите `app.js` и додајте нову функцију под називом `register`: ```js function register() { const registerForm = document.getElementById('registerForm'); const formData = new FormData(registerForm); const data = Object.fromEntries(formData); const jsonData = JSON.stringify(data); } ``` Овде преузимамо елемент формулара користећи `getElementById()` и користимо помоћни алат [`FormData`](https://developer.mozilla.org/docs/Web/API/FormData) за екстракцију вредности из контрола формулара као сет парова кључ/вредност. Затим конвертујемо податке у регуларни објекат користећи [`Object.fromEntries()`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) и на крају серијализујемо податке у [JSON](https://www.json.org/json-en.html), формат који се често користи за размену података на вебу. Подаци су сада спремни за слање серверу. Креирајте нову функцију под називом `createAccount`: ```js 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` овде. То значи да функција садржи код који ће се извршавати [**асинхроно**](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/async_function). Када се користи заједно са кључном речи `await`, омогућава чекање да се асинхрони код изврши - као што је чекање одговора сервера овде - пре него што се настави. Ево кратког видеа о коришћењу `async/await`: [![Async и Await за управљање промисима](https://img.youtube.com/vi/YwmlRkrxvkk/0.jpg)](https://youtube.com/watch?v=YwmlRkrxvkk "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()`: ```js const result = await createAccount(jsonData); ``` Пошто користимо кључну реч `await` овде, потребно је да додамо кључну реч `async` пре функције register: ```js async function register() { ``` На крају, додајмо неке логове да проверимо резултат. Коначна функција би требало да изгледа овако: ```js 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://developer.mozilla.org/docs/Learn/Common_questions/What_are_browser_developer_tools) и покушате да региструјете нови налог, не би требало да видите никакву промену на веб страници, али ће се порука појавити у конзоли која потврђује да све функционише. ![Снимак екрана који приказује поруку у конзоли претраживача](../../../../translated_images/browser-console.efaf0b51aaaf67782a29e1a0bb32cc063f189b18e894eb5926e02f1abe864ec2.sr.png) ✅ Да ли мислите да се подаци шаљу серверу безбедно? Шта ако неко успе да пресретне захтев? Можете прочитати о [HTTPS](https://en.wikipedia.org/wiki/HTTPS) да бисте сазнали више о безбедној комуникацији података. ## Валидација података Ако покушате да региструјете нови налог без прво постављања корисничког имена, можете видети да сервер враћа грешку са статусним кодом [400 (Bad Request)](https://developer.mozilla.org/docs/Web/HTTP/Status/400#:~:text=The%20HyperText%20Transfer%20Protocol%20(HTTP,%2C%20or%20deceptive%20request%20routing).). Пре слања података серверу добра је пракса [валидирати податке формулара](https://developer.mozilla.org/docs/Learn/Forms/Form_validation) унапред када је то могуће, како бисте били сигурни да шаљете валидан захтев. HTML5 контроле формулара пружају уграђену валидацију користећи различите атрибуте: - `required`: поље мора бити попуњено, иначе формулар не може бити послат. - `minlength` и `maxlength`: дефинишу минималан и максималан број карактера у текстуалним пољима. - `min` и `max`: дефинишу минималну и максималну вредност нумеричког поља. - `type`: дефинише врсту очекиваних података, као што су `number`, `email`, `file` или [други уграђени типови](https://developer.mozilla.org/docs/Web/HTML/Element/input). Овај атрибут може такође променити визуелни приказ контроле формулара. - `pattern`: омогућава дефинисање [регуларног израза](https://developer.mozilla.org/docs/Web/JavaScript/Guide/Regular_Expressions) за тестирање да ли су унети подаци валидни или не. Савет: можете прилагодити изглед контроле вашег формулара у зависности од тога да ли су валидне или не, користећи CSS псеудо-класе `:valid` и `:invalid`. ### Задатак Постоје два обавезна поља за креирање важећег новог налога: корисничко име и валута, док су остала поља опционална. Ажурирајте HTML форме, користећи и `required` атрибут и текст у ознаци поља тако да: ```html ... ``` Иако ова конкретна серверска имплементација не намеће специфична ограничења за максималну дужину поља, увек је добра пракса да се дефинишу разумна ограничења за било који унос текста од стране корисника. Додајте `maxlength` атрибут текстуалним пољима: ```html ... ... ``` Сада, ако притиснете дугме *Региструј се* и неко поље не поштује правило валидације које смо дефинисали, требало би да видите нешто овако: ![Снимак екрана који приказује грешку валидације приликом покушаја слања форме](../../../../translated_images/validation-error.8bd23e98d416c22f80076d04829a4bb718e0e550fd622862ef59008ccf0d5dce.sr.png) Валидација која се обавља *пре* слања било каквих података серверу назива се **валидација на страни клијента**. Али имајте на уму да није увек могуће обавити све провере без слања података. На пример, овде не можемо проверити да ли већ постоји налог са истим корисничким именом без слања захтева серверу. Додатна валидација која се обавља на серверу назива се **валидација на страни сервера**. Обично је потребно имплементирати обе врсте валидације, а док валидација на страни клијента побољшава корисничко искуство пружањем тренутних повратних информација кориснику, валидација на страни сервера је кључна за осигурање да су подаци корисника које обрађујете исправни и безбедни. --- ## 🚀 Изазов Прикажите поруку о грешци у HTML-у ако корисник већ постоји. Ево примера како коначна страница за пријаву може изгледати након мало стилизовања: ![Снимак екрана странице за пријаву након додавања CSS стилова](../../../../translated_images/result.96ef01f607bf856aa9789078633e94a4f7664d912f235efce2657299becca483.sr.png) ## Квиз након предавања [Квиз након предавања](https://ff-quizzes.netlify.app/web/quiz/44) ## Преглед и самостално учење Програмери су постали веома креативни у својим напорима за креирање форми, посебно када је реч о стратегијама валидације. Сазнајте више о различитим токовима форми прегледајући [CodePen](https://codepen.com); можете ли пронаћи неке занимљиве и инспиративне форме? ## Задатак [Стилизујте своју апликацију за банкарство](assignment.md) --- **Одрицање од одговорности**: Овај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције [Co-op Translator](https://github.com/Azure/co-op-translator). Иако настојимо да обезбедимо тачност, молимо вас да имате у виду да аутоматизовани преводи могу садржати грешке или нетачности. Оригинални документ на изворном језику треба сматрати ауторитативним извором. За критичне информације препоручује се професионални превод од стране људи. Не сносимо одговорност за било каква погрешна тумачења или неспоразуме који могу произаћи из коришћења овог превода.