Ciele
- Naučiť sa používať Fetch API na zmenu údajov na serveri a zakomponovať túto funkcionalitu do smerovačom na strane klienta riadenej jednostránkovej web aplikácie.
- Doplnenie jednostránkovej web aplikácie dynamického blogu o zobrazenie, pridávanie, odoberanie a editáciu článkov.
- Doplnenie jednostránkovej web aplikácie dynamického blogu o zobrazenie a pridávanie komentárov k článkom.
Úvod
Na tomto cvičení doplníte vašu jednostránkovú web aplikáciu dynamického blogu o pridávanie, odoberanie a editáciu jednotlivých článkov a zobrazenie a pridávanie komentárov k nim.
V "tutoriálovej" časti (krok 1 a 2 postupu) zobrazenie a editáciu článkov doplníte do vzorovej aplikácie, blogu o obľúbených stromoch. Potom to isté a ďalšiu funkcionalitu pridáte do vašej aplikácie, ktorá je výsledkom úlohy 5 (cvičenie 9).
Poznámka:
Všetko potrebné na splnenie dnešných úloh ste sa dozvedeli
z prednášok 7 až 10 ([1], [2],
[3], [4]) a z v nich uvedených príkladoch.
Pri riešení úloh používajte Fetch API.
Môžete použiť aj syntax s kľúčovými slovami async
a await
.
V plnom rozsahu je tiež potrebné využiť smerovač na strane klienta (hash router).
Postup
-
V prvej "tutoriálovej" úlohe si zobrazenie článkov skúsite pridať do príkladu blogu o obľúbených stromoch.Úloha: Do jednostránkovej web aplikácie blogu o obľúbených stromoch, ktorá je výsledkom kroku 2 a 3 z cvičenia 9, doplňte zobrazenie jednotlivých článkov.Úlohu môžete riešiť samostatne alebo môžete postupovať podľa nasledujúceho kompletného postupu riešenia:
Celý článok nech sa zobrazí po kliknutí na jeho názov v zozname. Zobrazenie implementujte pomocou smerovača, trasa nech má názovarticle
. Parametre trasy využite na uloženie id článku a údajov pre návrat na príslušnú stránku zoznamu článkov (tzn. tú, kde ste na názov klikli). Pod článkom zobrazte tlačidlá- Back pre návrat na príslušnú stránku zoznamu článkov,
- Edit pre editáciu článku a
- Delete pre vymazanie článku.
-
Stiahnite si a rozbaľte archív myDynamicBlogWArtEdit_starter.zip s aplikáciou. Ďalej budete pracovať s touto rozbalenou verziou aplikácie vo vhodnom IDE.
Táto aplikácia je výsledkom splnenia úloh z krokov 2 a 3 cvičenia 9 s pridaným modulom
articleFormsHandler.js
a drobnými úpravami v css a ďalších súboroch. ModularticleFormsHandler.js
obsahuje triedu na obsluhu formulárov pre pridávanie a editáciu článkov a vychádza zo skriptu v príklade [5].
-
Najprv do šablóny so zoznamom článkov doplníme odkazy na jednotlivé články.
Vindex.html
v elementescript
s id=template-articles
zmeňte riadok
na<h2>{{title}}</h2>
<h2><a href="{{detailLink}}">{{title}}</a></h2>
-
Teraz musíme do objektov, ktoré sa touto šablónou spracovávajú pridať do položky
Na koniecdetailLink
príslušné url fragmenty.routes.js
pridajte funkciu
Táto funkcia vyrobí potrebný url fragment. Jeho druhý a tretí parameter (function addArtDetailLink2ResponseJson(responseJSON){ responseJSON.articles = responseJSON.articles.map( article =>( { ...article, detailLink:`#article/${article.id}/${responseJSON.meta.offset}/${responseJSON.meta.totalCount}` } ) ); }
offset
atotalCount
) v tomto príklade síce nepotrebujeme, ale boli by užitočné ak by sme mali implementované stránkovanie (čo vaša aplikácia má mať).
-
Ďalej v
Vroutes.js
zavedieme konštanty s dôležitými nastaveniami - základ url servera a počet článkov na stránkuroutes.js
pred funkciucreateHtml4opinions
pridajte riadkyconst urlBase = "https://wt.kpi.fei.tuke.sk/api"; const articlesPerPage = 20;
-
Funkciu
VfetchAndDisplayArticles
vymeníme za novšiu verziu, ktorá pracuje s novými konštantami a tiež do objektov v zozname článkov pridá fragmenty, vytvorené funkciouaddArtDetailLink2ResponseJson
.routes.js
nahraďte funkciu
funkcioufunction fetchAndDisplayArticles(targetElm){ ... }
Názvy článkov sú teraz odkazmi so správnymi trasami, no pre tieto trasy ešte nie je spracovanie.function fetchAndDisplayArticles(targetElm, offsetFromHash, totalCountFromHash){ const offset=Number(offsetFromHash); const totalCount=Number(totalCountFromHash); let urlQuery = ""; if (offset && totalCount){ urlQuery=`?offset=${offset}&max=${articlesPerPage}`; }else{ urlQuery=`?max=${articlesPerPage}`; } const url = `${urlBase}/article${urlQuery}`; 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 => { addArtDetailLink2ResponseJson(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 ); }); }
-
Implementáciu spracovania trás začneme pridaním Mustache šablóny pre článok a tlačidlá pod ním.
Doindex.html
za element
pridajte element<script id="template-addOpinion" type="text/template"> ... </script>
<script id="template-article" type="text/template"> <article> <h3>{{title}}</h3> <p> by {{author}} </p> {{#imageLink}} <figure> <img src="{{imageLink}}" alt="article figure" /> </figure> {{/imageLink}} <div>{{{content}}}</div> <p> Keywords: {{tags}} </p> </article> <footer> <a href="{{backLink}}" class="linkAsButton"><< Back</a> <a href="{{editLink}}" class="linkAsButton">Edit</a> <a href="{{deleteLink}}" class="linkAsButton">Delete</a> <a href="#menuTitle" class="linkAsButton">Up to the menu</a> </footer> </script>
-
Teraz pridáme objekt s trasou.
Do poľa s trasami vroutes.js
pridajte objekt
pre trasu{ hash:"article", target:"router-view", getTemplate: fetchAndDisplayArticleDetail }
article
.
-
Do
Na koniecroutes.js
ešte vložíme funkcie na spracovanie trasy.routes.js
pridajte kód
Funkciafunction fetchAndDisplayArticleDetail(targetElm,artIdFromHash,offsetFromHash,totalCountFromHash) { fetchAndProcessArticle(...arguments,false); } /** * Gets an article record from a server and processes it to html according to * the value of the forEdit parameter. Assumes existence of the urlBase global variable * with the base of the server url (e.g. "https://wt.kpi.fei.tuke.sk/api"), * availability of the Mustache.render() function and Mustache templates ) * with id="template-article" (if forEdit=false) and id="template-article-form" (if forEdit=true). * @param targetElm - id of the element to which the acquired article record * will be rendered using the corresponding template * @param artIdFromHash - id of the article to be acquired * @param offsetFromHash - current offset of the article list display to which the user should return * @param totalCountFromHash - total number of articles on the server * @param forEdit - if false, the function renders the article to HTML using * the template-article for display. * If true, it renders using template-article-form for editing. */ function fetchAndProcessArticle(targetElm,artIdFromHash,offsetFromHash,totalCountFromHash,forEdit){ const url = `${urlBase}/article/${artIdFromHash}`; 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 => { if(forEdit){ }else{ responseJSON.backLink=`#articles/${offsetFromHash}/${totalCountFromHash}`; responseJSON.editLink= `#artEdit/${responseJSON.id}/${offsetFromHash}/${totalCountFromHash}`; responseJSON.deleteLink= `#artDelete/${responseJSON.id}/${offsetFromHash}/${totalCountFromHash}`; document.getElementById(targetElm).innerHTML = Mustache.render( document.getElementById("template-article").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 ); }); }
fetchAndDisplayArticleDetail
iba volá funkciufetchAndProcessArticle
s parametromforEdit=false
. FunkciufetchAndProcessArticle
sme si už pripravili tak, aby nám mohla poslúžiť aj pri napĺňaní formulára údajmi z článku keď budeme chcieť článok editovať.
Tým je úloha splnená. Tlačidlá Edit a Delete majú už správne odkazy (url fragmenty), ale zatiaľ bez spracovania.
-
Teraz v druhej "tutoriálovej" úlohe do príkladu pridáme editáciu článku.Úloha: Do jednostránkovej web aplikácie blogu o obľúbených stromoch z predchádzajúceho kroku doplňte editáciu článkov.
Editácia článku nech je dostupná cez v predchádzajúcom kroku pridané tlačidlo Edit a nech je implementovaná pomocou smerovača v trase s názvomartEdit
.-
Začneme znova šablónou, tentokrát pre formulár článku.
Doindex.html
za element
pridajte element<script id="template-article" type="text/template"> ... </script>
<script id="template-article-form" type="text/template"> <article> <h2>{{formTitle}}</h2> <br><br> <form id="articleForm"> <label for="author">Author:</label> <input type="text" name="author" id="author" value="{{author}}" size="50" title="Article author, max. length 100 characters." maxlength="100" placeholder="e.g. Ján Trieska" /> <br> <label for="title">Title:</label> <input type="text" name="title" id="title" value="{{title}}" size="50" maxlength="100" pattern="\S[\S\s]*" placeholder="e.g. My story." required title="Article title, mandatory item, max. length: 100 characters. The first character must not be a space." /> <br> <label for="imageLink">Image (url):</label> <input type="url" name="imageLink" id="imageLink" value="{{imageLink}}" size="50" title="Image URL, max. length 100 characters." maxlength="100"/> <br> <label></label> <button type="button" id="btShowFileUpload"> Upload image </button> <fieldset class="added hiddenElm" id="fsetFileUpload"> <legend>Image Upload</legend> <input type="file" id="flElm" name="file" accept="image/jpeg, image/png"/> <br /> <button type="button" id="btFileUpload"> Send image to server </button> <button type="button" id="btCancelFileUpload"> Cancel uploading </button> </fieldset> <br> <label for="content">Article content:</label> <textarea name="content" id="content" spellcheck="true" lang="sk" cols="50" rows="20" required title="Article content, mandatory item, can be plain text or in HTML."> {{content}} </textarea> <br> <label for="tags">Keywords:</label> <input type="text" name="tags" id="tags" value="{{tags}}" size="50" title="Keyword list, comma separated." placeholder="e.g. village, drama" /> <br> <br> <button type="reset"> Reset Form </button> <button type="submit"> {{submitBtTitle}} </button> </form> </article> <footer> <a href="{{backLink}}" class="linkAsButton"><< Back</a> <a href="#menuTitle" class="linkAsButton">Up to the menu</a> </footer> </script>
-
Ďalej pridáme objekt s trasou
Do poľa s trasami vartEdit
.routes.js
pridajte objekt
pre trasu{ hash:"artEdit", target:"router-view", getTemplate: editArticle }
artEdit
.
-
Teraz doplníme funkciu
DoeditArticle
, ktorá volá funkciufetchAndProcessArticle
s parametromforEdit=true
.routes.js
, na koniec alebo pred funkciufetchAndProcessArticle
, pridajte funkciufunction editArticle(targetElm, artIdFromHash, offsetFromHash, totalCountFromHash) { fetchAndProcessArticle(...arguments,true); }
-
Ďalej musíme do funkcie
VfetchAndProcessArticle
doplniť vetvu pre prípad editácie.routes.js
vo funkciifetchAndProcessArticle
kód
nahraďte kódomif(forEdit){ }else{
if(forEdit){ responseJSON.formTitle="Article Edit"; responseJSON.submitBtTitle="Save article"; responseJSON.backLink=`#article/${artIdFromHash}/${offsetFromHash}/${totalCountFromHash}`; document.getElementById(targetElm).innerHTML = Mustache.render( document.getElementById("template-article-form").innerHTML, responseJSON ); if(!window.artFrmHandler){ window.artFrmHandler= new articleFormsHandler("https://wt.kpi.fei.tuke.sk/api"); } window.artFrmHandler.assignFormAndArticle("articleForm","hiddenElm",artIdFromHash,offsetFromHash,totalCountFromHash); }else{
-
Nakoniec v
Doindex.html
pridáme načítanie skriptu v ktorom máme kód pre obsluhu formulára.index.html
za element
pridajte element<script src="js/addOpinion.js"></script>
<script src="js/articleFormsHandler.js"></script>
-
-
Teraz manipuláciu s jednotlivými článkami zavediete aj do vašej aplikácie.Úloha: Rozšírte vašu jednostránkovú web aplikáciu o zobrazenie, editáciu, vymazanie a pridávanie jednotlivých článkov.
-
Celý článok nech sa zobrazí po kliknutí na jeho názov v zozname.
Zobrazenie implementujte pomocou smerovača, trasa nech má názov
article
. Parametre trasy využite na uloženie id článku a údajov pre návrat na príslušnú stránku zoznamu článkov (tzn. tú, kde ste na názov klikli). Pod článkom zobrazte tlačidlá- Back pre návrat na príslušnú stránku zoznamu článkov,
- Edit pre editáciu článku a
- Delete pre vymazanie článku.
-
Editácia článku nech je dostupná cez vyššie spomenuté tlačidlo Edit
a nech je implementovaná pomocou smerovača v trase s názvom
artEdit
. -
Vymazanie článku nech je dostupné cez vyššie spomenuté tlačidlo Delete
a nech je implementované pomocou smerovača v trase s názvom
artDelete
. -
Pridanie nového článku nech je dostupné z hlavného menu aplikácie, v položke Add article
a nech je implementované pomocou smerovača v trase s názvom
artInsert
.
articleFormsHandler.js
v tutoriálových úlohách).Poznámka: Ako implementovať zobrazenie a editáciu článku ste sa naučili v predchádzajúcich "tutoriálových" úlohách.
Poznámka: Teraz môžete na server zaviesť aj vaše články, ktoré ste tvorili v úlohe 1. Ako obsah článkov môžete vkladať aj HTML kód. Ak chcete aby sa vám zobrazovali iba vaše články, realizujte druhú doplnkovú úlohu.
Poznámka: Nezabudnite, že vaša aplikácia má stále mať funkcionalitu, pridanú v cvičení 9. To znamená stránkovaný zoznam článkov a zobrazovanie obsahov článkov v zozname. Ak sa vám nepáči zobrazovať celé obsahy článkov, zobrazte namiesto nich náhľady obsahov. Ako na zobrazenie náhľadov vidíte v príklade 72_04artListDetailPrevComFetch.
-
Celý článok nech sa zobrazí po kliknutí na jeho názov v zozname.
Zobrazenie implementujte pomocou smerovača, trasa nech má názov
-
A nakoniec do vašej aplikácie pridáte manipuláciu s komentármi k článkom.Úloha: Doplňte vašu jednostránkovú web aplikáciu o zobrazenie a pridávanie komentárov k článkom.
Komentáre nech sa vždy zobrazia pod článkom - je teda potrebné doplniť trasuarticle
.
Pod komentármi nech je tlačidlo Add Comment po stlačení ktorého nech sa zobrazí formulár pre nový komentár. Pri pridávaní nového komentára nech je stále viditeľný aspoň článok.Poznámka: Stačí keď zobrazíte prvých maximálne 100 komentárov. Pre editáciu nemusíte používať smerovač.
Poznámka: Ak by sme zobrazenie komentárov pridali týmto spôsobom do blogu o obľúbených stromoch, mohlo by to vyzerať nasledovne:
Zdroje
- Prednáška 7.
- Prednáška 8.
- Prednáška 9.
- Prednáška 10.
- 75_02articleAddFetchImgUpload.html - príklad nahratia údajov na server pomocou Fetch API a HTTP metódy POST.
- API dokumentácia poskytnutého servera.
Doplňujúce úlohy
Poznámka:
Trasu môžete ponechať tú istú ako v povinných úlohách (tzn. article
).
Bude však potrebné pridať ďalšie parametre trasy, pre stránkovanie komentárov.
Poznámka:
Pri získavaní komentárov zo servera viete v url použiť parametre max
a offset
,
podobne ako pri zozname článkov. Bližšie informácie sú v dokumentácii servera
[6].
Poznámka:
Pre stránkovanie komentárov môžete využiť aj samostatnú trasu,
napr. s názvom artComment
.
Komentáre však stále majú byť zobrazené pod článkom.
Preto by ste v tejto trase mali použiť iný cieľový element ako v ostatných.
Poznámka:
Parametre vyhľadávania viete kombinovať aj s ďalšími ako napr.max
a offset
.
Príklad: http://wt.kpi.fei.tuke.sk/api/article?tag=skrArt20&max=2&offset=1