Ciele
- Naučiť sa tvoriť jednostránkové aplikácie (single page applications) použitím smerovača na strane klienta (client side router) [1].
- Naučiť sa získavať údaje zo servera pomocou HTTP metódy GET a Fetch API [3], [4], [5].
- Aplikovať smerovač na strane klienta a získavanie údajov zo servera pomocou Fetch API vo vlastnej webovej aplikácii dynamického blogu(Úloha 5).
Úvod
Na tomto a ďalších cvičeniach budete pracovať na druhom zadaní - webovej aplikácii dynamického blogu.
Váš blog bude jednostránkovou aplikáciou (single page application, SPA) s jediným HTML súborom (index.html
).
Navigáciu bude zabezpečovať smerovač na strane klienta (client side router),
ktorý bude využívať časť url nazývanú fragment alebo hash (za znakom #).
Všetko si najprv vyskúšate formou tutoriálu na aplikácii, ktorá je podobná výsledku kroku 2 z cvičenia 7. Tutoriál je obsiahnutý v krokoch 1 až 3 posupu.
Vznikne webová aplikácia, ktorú potom prerobíte do podoby podobnej vášmu prvému zadaniu - statickému blogu. Z prvého zadania prevezmete tému, vzhľad a názory návštevníkov. Výslednú aplikáciu rozšírite o získavanie ďalších údajov zo servera a ďalej ju budete rozširovať v nasledujúcich týždňoch.
Poznámka:
Všetko potrebné na splnenie dnešných úloh ste sa dozvedeli z prednášok 7 až 9 ([1], [2], [3]) a z v nich uvedených príkladoch.
S problematikou, ktorú máte dnes zvládnuť vás podrobne oboznámia aj prvé tri úlohy (kroky) tohto cvičenia.
Pri riešení úloh používajte Fetch API. Môžete použiť aj syntax s kľúčovými slovami async
a await
.
Poznámka:
Toto cvičenie sa od predchádzajúcich aj nasledujúcich lýši v jednej podstatnej veci. Kým v ostatných sú "tutoriálové"
úlohy doplňujúce a môžete ich preskočiť, tu sú dôležitou súčasťou tvorby vášho zadania.
To, čo v nich vytvoríte bude tvoriť základ finálnej podoby vášho zadania.
Postup
-
Najprv sa pozriete na rozšísenú verziu hash router-a, ktorý ste videli na prednáške [1]. Tento router budete používať v ďalších krokoch.Úloha: Oboznámte sa s parametrickým hash router-om 06hashRouterModularPaged.
Stiahnuteľná verzia router-a je dostupná v archíve 06hashRouterModularPaged.zip.
Všimnite si, že oproti hash router-u z prednášky [1] je tento načítavaný ako modul a jednotlivé trasy (v
routes.js
) majú v položkegetTemplate
funkciu. Funkciu preto, aby sme mohli HTML tvoriť na základe parametrov. Funkcia sa navyše stará aj o vloženie HTML do cieľového elementu (položkatarget
). To je preto, aby sme zvládli aj spracovanie údajov získaných pomocou asynchrónnych požiadaviek zo servera.Poznámka: Dôsledkom načítania router-a ako modulu je, že príklad bude funkčný iba pri spustení na serveri. Preto na experimentovanie použite vhodné IDE, napríklad WebStorm alebo VS Code s LiveServer. To platí aj pre aplikáciu, ktorú budete vytvárať v ďalších krokoch.
-
Teraz vytvoríte na klientovi smerovanú verziu blogu o obľúbených stromoch.Úloha: Vytvorte takú verziu jednostránkovej aplikácie z kroku 2 v cvičení 7, kde bude riadenie stavu aplikácie zabezpečené hash router-om. Všetky trasy budú dostupné z hlavného menu aplikácie. Trasy budú 4:Úlohu môžete riešiť samostatne alebo môžete postupovať podľa nasledujúceho kompletného postupu riešenia. Prvý krok postupu však musíte vykonať, keďže v ňom vytvoríte základ druhého projektu
-
welcome
(odkaz Welcome!) pre uvítací článok -
articles
(odkaz Articles) pre články o stromoch -
opinions
(odkaz Visitor opinions) pre názory návštevníkov a -
addOpinion
(odkaz Add your opinion) pre formulár na zadanie názoru.
-
Použite odkaz https://kurzy.kpi.fei.tuke.sk/gitlab-classroom/wt2 na vytvorenie druhého projektu v katedrovom GitLab-e. Postupujte rovnakým spôsobom ako pri prvom projekte v cvičení 1. Ďalej budete pracovať s týmto projektom vo vhodnom IDE.
V prípade problémov s vytvorením projektu v katedrovom GitLab-e použite archív myDynamicBlog_starter.zip.
Vytvorili ste základ druhého projektu, ktorý už obsahuje js súbory s routerom (
paramHashRouter.js
), trasami (routes.js
), kódom pre hlavné menu (mainMenu.js
) a spracovanie údajov z formulára (addOpinion.js
). SkriptrouterInit.js
inicializuje router. Funkčná je len prvá položka v menu (Welcome!).
-
Do
index.html
za element
pridajte element<script id="template-welcome" type="text/template"> ... </script>
so šablónou pre články.<script id="template-articles" type="text/template"> <article id="artPine"> <h2>Pine</h2> <figure> <img src="fig/pineBw.png" height="150" title="fig.pine" alt="pine" /> </figure> <p>Pine is a softwood coniferous tree.</p> <dl> <!-- zoznam moze byt iba mimo odsekov --> <dt>Latin name: <!-- uzatvaracia znacka nemusi byt uvedena --> <dd>Pinus <dt>Division:</dt> <dd>Pinophyta</dd> <dt>Class:</dt> <dd>Pinopsida</dd> </dl> </article> <article id="artOak"> <h2>Oak</h2> <figure> <img src="fig/oak.png" height="150" title="fig.oak" alt="oak" /> </figure> <p> Oak is a deciduous tree with hardwood. It lives long and grows slowly. Its leaves are simple, lobate and fall off before the winter. </p> <footer class="menuLink"><a href="#menuTitle">Back to the menu</a></footer> </article> </script>
To, čo sme pridali síce nazývame šablóna, ale v tomto prípade je to len HTML kód, ktorý sa pri kliknutí na odkaz Articles vloží do príslušného HTML elementu.
-
Do poľa v
routes.js
pridajte objekt
Obsah{ hash:"articles", target:"router-view", getTemplate:(targetElm) => document.getElementById(targetElm).innerHTML = document.getElementById("template-articles").innerHTML }
routes.js
teraz bude
Teraz je funkčná aj druhá položka v menu (Articles).//an array, defining the routes export default[ { //the part after '#' in the url (so-called fragment): hash:"welcome", ///id of the target html element: target:"router-view", //the function that returns content to be rendered to the target html element: getTemplate:(targetElm) => document.getElementById(targetElm).innerHTML = document.getElementById("template-welcome").innerHTML }, { hash:"articles", target:"router-view", getTemplate:(targetElm) => document.getElementById(targetElm).innerHTML = document.getElementById("template-articles").innerHTML } ];
Funkcia, ktorá je v položkegetTemplate
nového objektu iba načíta html obsahscript
elementu s id=template-welcome
do cieľového elementu. Cieľový element je ten s id daným argumentomtargetElm
, tu je torouter-view
. Ak chcete vedieť ako sarouter-view
dostane dotargetElm
, preštudujte si kód router-a, konkrétneparamHashRouter.js
.
-
Do
index.html
za element
pridajte element<script id="template-articles" type="text/template"> ... </script>
s Mustache šablónou pre názory návštevníkov.<script id="template-opinions" type="text/template"> <article id="artOpinions"> <h2>Visitor Opinions</h2> {{#.}} <section> <h3>{{name}} <i>{{createdDate}}</i></h3> <p>{{comment}}</p> <p>{{willReturn}}</p> </section> {{/.}} {{^.}} <section> Sorry, no opinions found. </section> {{/.}} <footer class="menuLink"><a href="#menuTitle">Back to the menu</a></footer> </article> </script>
-
Do poľa v
routes.js
pridajte objekt
Na spracovanie názorov návštevníkov do HTML potrebujeme viac ako jeden riadok kódu. Aby to bolo prehľadné, dáme tento kód do samostatnej funkcie{ hash:"opinions", target:"router-view", getTemplate: createHtml4opinions }
createHtml4opinions
. Túto funkciu pridáme v ďalšom kroku. Teraz sme len v položkegetTemplate
objektu s príslušnou trasou (opinions
) uviedli jej názov.
-
Na koniec
routes.js
pridajte funkciu
Funkčná je už aj tretia položka v menu (Visitor opinions).function createHtml4opinions(targetElm){ const opinionsFromStorage=localStorage.myTreesComments; let opinions=[]; if(opinionsFromStorage){ opinions=JSON.parse(opinionsFromStorage); opinions.forEach(opinion => { opinion.created = (new Date(opinion.created)).toDateString(); opinion.willReturn = opinion.willReturn?"I will return to this page.":"Sorry, one visit was enough."; }); } document.getElementById(targetElm).innerHTML = Mustache.render( document.getElementById("template-opinions").innerHTML, opinions ); }
-
Do
index.html
za element
pridajte element<script id="template-opinions" type="text/template"> </script>
čo je HTML kód pre formulár pre nový názor.<script id="template-addOpinion" type="text/template"> <article id="artOpnFrm"> <h2>Your Opinion</h2> <p> Please, use the form below to state your opinion about this page. </p> <form id="opnFrm"> <label for="nameElm">Your name:</label> <input type="text" name="login" id="nameElm" size="20" maxlength="50" placeholder="Enter your name here" required /> <br><br> <label for="opnElm">Your opinion:</label> <textarea name="comment" id="opnElm" cols="50" rows="3" placeholder="Express your opinion here" required></textarea> <br><br> <input type="checkbox" id="willReturnElm" /> <label for="willReturnElm">I will definitely return to this page.</label> <br><br> <button type="submit">Send</button> </form> <footer class="menuLink"><a href="#menuTitle">Back to the menu</a></footer> </article> </script>
Podobne, ako v prípadetemplate-articles
to ani nie je skutočná šablóna, keďže neobsahuje žiadne značky, ktoré by sa nahrádzali údajmi z JavaScript objektov.
-
Do poľa v
routes.js
pridajte objekt
Tým ste ukončili sfunkčnenie poslednej položky v menu. Kód na spracovanie údajov z formulára už totiž je v súbore{ hash:"addOpinion", target:"router-view", getTemplate: (targetElm) =>{ document.getElementById(targetElm).innerHTML = document.getElementById("template-addOpinion").innerHTML; document.getElementById("opnFrm").onsubmit=processOpnFrmData; } }
addOpinion.js
.
-
-
Ďalej vymeníte statické články za dynamické zo servera
https://wt.kpi.fei.tuke.sk/
. Teda, za ich názvy a autorov.Úloha: V trasePostup riešenia je nasledovný:articles
nahraďte statické články za zoznam prvých maximálne 10 článkov zo serverahttps://wt.kpi.fei.tuke.sk/
. Ku každému zobrazte názov (title
) a autora (author
). Na získanie článkov použite Fetch API.Poznámka: Všimnite si, že tu používame
https
verziu servera. Ten je dostupný v oboch verziách,http://wt.kpi.fei.tuke.sk/
ajhttps://wt.kpi.fei.tuke.sk/
. Odporúčame použiťhttps
verziu, aby ste nemali problém s funkcionalitou riešenia pri umiestnení online cez codesandbox, glitch alebo iné podobné služby.
-
V
index.html
vymažte element
a namiesto neho pridajte elementy<script id="template-articles" type="text/template"> ... </script>
Nová šablóna<script id="template-articles" type="text/template"> {{#articles}} <article> <h2>{{title}}</h2> <p> by {{author}} </p> </article> {{/articles}} <footer class="menuLink"><a href="#menuTitle">Back to the menu</a></footer> </script> <script id="template-articles-error" type="text/template"> <article> <h2>Articles acquisition failed</h2> <p> {{errMessage}} </p> </article> <footer class="menuLink"><a href="#menuTitle">Back to the menu</a></footer> </script>
template-articles
slúži na zobrazenie zoznamu článkov, získaných zo servera. Šablónatemplate-articles-error
sa použije keď sa články získať nepodarí a to na výpis chybového hlásenia.
-
V poli v
routes.js
zmeňte riadky
nagetTemplate:(targetElm) => document.getElementById(targetElm).innerHTML = document.getElementById("template-articles").innerHTML
Podobne ako pri trasegetTemplate: fetchAndDisplayArticles
opinions
, aj tu spracovanie dáme do samostatnej funkcie, konkrétnefetchAndDisplayArticles
. Túto funkciu pridáme v ďalšom kroku.
-
Na koniec
routes.js
pridajte funkciu
Funkciafunction fetchAndDisplayArticles(targetElm){ const url = "https://wt.kpi.fei.tuke.sk/api/article"; fetch(url) .then(response =>{ if(response.ok){ return response.json(); }else{ //if we get server error return Promise.reject( new Error(`Server answered with ${response.status}: ${response.statusText}.`)); } }) .then(responseJSON => { document.getElementById(targetElm).innerHTML = Mustache.render( document.getElementById("template-articles").innerHTML, responseJSON ); }) .catch (error => { ////here we process all the failed promises const errMsgObj = {errMessage:error}; document.getElementById(targetElm).innerHTML = Mustache.render( document.getElementById("template-articles-error").innerHTML, errMsgObj ); }); }
fetchAndDisplayArticles
získa údaje o článkoch zo servera pomocou Fetch API, vygeneruje z nich HTML kód použitím Mustache šablóny a vloží ho do elementu s id vo vstupnej premennejtargetElm
.
-
V
-
Úloha: Aplikáciu z predchádzajúceho kroku upravte a rozšírte použitím vášho prvého projektu. Zachovajte pri tom použitie routera v plnom rozsahu.V tejto úlohe máte s použitím toho, čo ste vytvorili v cvičeniach 1 až 7 urobiť s aplikáciou z predchádzajúceho kroku nasledujúce:
-
Názov stránky a uvítaciu časť
(obsah šablóny s id=
template-welcome
) zmeňte tak, aby zodpovedali vašej téme. -
Formulár v trase
addOpinion
vymeňte za váš z úlohy 3 (cvičenie 6). Z vášho riešenia úloh 3 a 4 tiež prevezmite spracovanie obsahu formulára do localStorage. -
Zobrazenie názorov v trase
opinions
zmeňte na to vaše, z úlohy 4 (cvičenie 7).
HTML kód však definujte pomocou Mustache šablóny a to aj v prípade ak ste v úlohe 4 použili šablónové literály. Použite šablónu s id=template-opinions
. - Na celú stránku (aplikáciu) aplikujte vaše vlastné CSS štýly. Môžu ale nemusia to byť tie, čo ste vytvorili pre váš prvý projekt (cvičenia 2 až 6).
Poznámka: Jediné, čo nebude v súlade s vašou témou sú články načítané so servera. To ale budete môcť zmeniť v doplňujúcej úlohe v cvičení 11.
-
Názov stránky a uvítaciu časť
(obsah šablóny s id=
-
Úloha: V aplikácii chýbajú telá článkov (položka
content
). Doplňte ich vykonaním príslušných požiadaviek na server a zobrazte v HTML. Kód doplňte do funkciefetchAndDisplayArticles
a za účelom zobrazenia rozšírte šablónu s id=template-articles
.
Zachovajte pri tom použitie routera v plnom rozsahu a využite Fetch API.Poznámka: Ako vnárať požiadavky s fetch a promises a následne zobraziť výsledky vidíte v príklade 72_03artListDetailComFetch.html.
POZOR: Príklad 72_03artListDetailComFetch.html získava a zobrazuje aj komentáre. Tu ale komentáre zobrazovať nechceme, preto ak využijete kód z tohto príkladu, musíte ho upraviť. (Zobrazenie komentárov pridáme v cvičení 11). -
Úloha: V aplikácii sa teraz zobrazí iba prvých 10 článkov. Zmeňte to tak aby bolo možné zobraziť všetky články. Články nech sa zobrazujú po stránkach, kde na každej stránke (okrem poslednej) bude 20 článkov. Pomocou odkazov Previous a Next nech sa dá prejsť na stránku s predchádzajúcimi resp. nasledujúcimi (maximálne) 20 článkami. Ak sa prejsť na nasledujúce alebo predchádzajúce už nedá, príslušný odkaz nech sa nezobrazí. Prechádzanie riešte pomocou router-a a Mustache šablón. Pre komunikáciu so serverom použite Fetch API.
Poznámka: Stránkovanie s router-om vidíte v príklade 06hashRouterModularPaged (06hashRouterModularPaged.zip) v trase
main
(odkaz Main Content).
Ako získať požadovaný počet článkov od požadovaného indexu (offset) zistíte z dokumentácie servera [7] (časť Parametre pre rozsah článkov).
Zdroje
- Prednáška 7.
- Prednáška 8.
- Prednáška 9.
- Fetch API at MDN web docs (tutorials and reference), https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API.
- Promises, fetch and async/await at The Modern JavaScript Tutorial, https://javascript.info/async.
- Virtuálny stroj s nainštalovaným serverom (pre tvorbu zadania bez pripojenia na internet). Po stiahnutí je ho možné spustiť v prostredí VirtualBox.
- API dokumentácia poskytnutého servera.