7. diel - Vylepšenia objektového diáre v JavaScripte
V minulej lekcii, Objekty, JSON a vylepšenia diáre v JavaScripte , sme si vylepšili náš diár o ukladaní do
localStorage
. Dnes doplníme radenie záznamov podľa dátumu,
zoskupovanie záznamov s rovnakým dátumom a odstránenie záznamov.
Výpis záznamov
Najprv si upravíme výpis našich záznamov. Bolo by vhodné záznamy zoradiť podľa dátumu a zoskupiť záznamy v rovnaký deň, ktoré vypíšeme pod sebou v jednom bloku.
Radenie
Najskôr si naimplementujeme metódu na radenie záznamov. K tomu použijeme
metódu sort()
na poli. Tá dokáže pole zoradiť postupným
porovnávaním dvojíc prvkov. Ako parameter prijíma porovnávaciu funkciu,
ktorá definuje akým spôsobom sa majú 2 prvky v poli porovnať. Naša metóda
k zoradenie záznamov v poli podľa dátumu bude v triede Diar
vyzerať nasledovne:
seradZaznamy() { this.zaznamy.sort(function (zaznam1, zaznam2) { return (new Date(zaznam1.datum) - new Date(zaznam2.datum)); }); }
Funkcia porovnáva dáta dvoch záznamov, ktorá si naparsujeme na dátum
pomocou konštruktory objektu Date
. Zo záznamu samozrejme
vyberieme vlastnosť datum
. Porovnanie dvoch dátumov vykonáme
jednoducho pomocou operátora -
, čím sa vráti ich rozdiel v
milisekundách. Pokiaľ bude prvý dátum až po druhom, vráti sa záporné
číslo. Pokiaľ budú rovnaké, vráti sa 0
. Inak sa vráti
kladné číslo. Práve kladné, záporné alebo nulové číslo metóda
sort()
pre svoju prácu potrebuje a tým zistí, či je dátum
väčšie, menšie alebo rovné.
Metódu budeme volať pred každým výpisom záznamov:
vypisZaznamy() { this.seradZaznamy(); this.vypisElement.innerHTML = ""; for (let i = 0; i < this.zaznamy.length; i++) { const zaznam = this.zaznamy[i]; this.vypisElement.innerHTML += `<h3>${zaznam.nazev}</h3>kdy: ${zaznam.datum}<br>splněno: ${zaznam.splneno}`; } }
Zoradenie by sme mohli tiež volať po pridaní záznamu a po ich načítaní, aby sa nemuselo volať pri každom výpise. Nevýhodou tohto riešenia by však bolo, že by sme na neho mohli zabudnú pri inej manipulácii so záznamami.
Ak teraz diár spustíme a máme v localStorage
už nejaké
dáta, vypíšu sa nám už zoradené podľa dátumu.
Zoskupovanie
Teraz výpis záznamov dokončíme. Budeme teda vypisovať dátum ak tomuto dátumu vždy všetky záznamy v daný deň. Náš cyklus ľahko upravíme:
vypisZaznamy() { this.seradZaznamy(); this.vypisElement.innerHTML = ""; let posledniDatum = null; for (const zaznam of this.zaznamy) { if (zaznam.datum !== posledniDatum) { this.vypisElement.innerHTML += `<h3>${zaznam.datum}</h3>` } posledniDatum = zaznam.datum; this.vypisElement.innerHTML += `<strong>${zaznam.nazev}</strong><br>splněno: ${zaznam.splneno}<hr>`; } }
Do premennej posledniDatum
priradíme vlastnosť
datum
z predchádzajúceho záznamu. Pretože pri prvom priebehu
cyklu posledný záznam ešte nie je, nastavíme premennú prvýkrát na
null
. Dátum súčasného záznamu potom vypíšeme len ak sa
líši od predchádzajúceho. Tak sa záznamy v rovnaký deň budú zoskupovať.
Teraz máme vypísané pekne úlohy s rovnakým dátumom pod sebou a
zoradené:
Formátovanie dáta a splněnosti
Keďže formát dátumu a splněnost úlohy tiež nie je ideálne, je
potrebné tieto výpisy upraviť do "ľudskejší" podoby Pamätáte na vlastnosť
jazyk
, ktorú môžeme ovplyvniť v konstruktoru? Teraz jej
využijeme k lepšiemu výpisu dáta a zároveň si vylepšíme výpis
splněnosti úloh:
vypisZaznamy() { this.seradZaznamy(); this.vypisElement.innerHTML = ""; let posledniDatum = null; for (const zaznam of this.zaznamy) { if (zaznam.datum !== posledniDatum) { const datum = new Date(zaznam.datum).toLocaleDateString(this.jazyk, { weekday: "long", day: "numeric", month: "short", year: "numeric" }); this.vypisElement.innerHTML += `<h3>${datum}</h3>` } posledniDatum = zaznam.datum; this.vypisElement.innerHTML += `<strong>${zaznam.nazev}</strong><br>úkol ${!zaznam.splneno ? "ne" : ""}splněn<hr>`; } }
Vytvoríme inštanciu objektu Date
z nášho dátumu a
použijeme jej metódu toLocaleString()
, kam odovzdáme ako prvý
parameter vlastnosť jazyk
našej triedy a ako druhý parameter
formátovacie objekt, ktorého vlastnosti udávajú ako presne sa má dátum
vypísať.
U splněnosti úlohy sme použili jednoduchý ternárne operátor, podľa
nesplněnosti pridáme "ne"
alebo prázdny string
.
Výsledok teraz vyzerá takto:
Mazanie záznamov
Budeme pokračovať základné interakcií s našimi úlohami, budeme ich môcť mazať, alebo ich označiť ako splnené. Začneme mazaním.
Uloženie záznamov
Keďže po zmazaní bude potrebné záznamy znova uložiť, vyčlení si
uloženie záznamov z metódy nastavUdalosti()
do samostatnej
metódy ulozZaznamy()
:
ulozZaznamy() { localStorage.setItem("zaznamy", JSON.stringify(this.zaznamy)); }
V metóde nastavUdalosti()
teraz metódu
ulozZaznamy()
zavoláme:
nastavUdalosti() { this.potvrditButton.onclick = () => { // this zůstane nyní stále this const zaznam = new Zaznam(this.nazevInput.value, this.datumInput.value); this.zaznamy.push(zaznam); this.ulozZaznamy(); this.vypisZaznamy(); }; }
Tlačidlo
Ku každému záznamu vygenerujeme tlačidlo na jeho odstránenie. To
vytvoríme ako nový element <button>
pomocou metódy
document.createElement()
a do <div>
us výpisom
záznamov ho vložíme pomocou appendChild()
. Tlačidlu takisto
pridáme udalosť reakcie na kliknutie, kedy daný záznam odstránime z poľa,
záznamy takto přeuložíme do localStorage
a znova vypíšeme.
Metóda vypisZaznamy()
bude po pridaní mazacieho tlačidla
vyzerať takto:
vypisZaznamy() { this.seradZaznamy(); this.vypisElement.innerHTML = ""; let posledniDatum = null; for (const zaznam of this.zaznamy) { if (zaznam.datum !== posledniDatum) { const datum = new Date(zaznam.datum).toLocaleDateString(this.jazyk, { weekday: "long", day: "numeric", month: "short", year: "numeric" }); this.vypisElement.innerHTML += `<h3>${datum}</h3>` } posledniDatum = zaznam.datum; this.vypisElement.innerHTML += `<strong>${zaznam.nazev}</strong> <br>úkol ${!zaznam.splneno ? "ne" : ""}splněn`; const smazatButton = document.createElement("button"); smazatButton.onclick = () => { if (confirm("Opravdu si přejete odstranit úkol?")) { this.zaznamy = this.zaznamy.filter(z => z !== zaznam); // Ponechá vše co není rovné proměnné zaznam this.ulozZaznamy(); this.vypisZaznamy(); } }; smazatButton.innerText = "Smazat záznam"; this.vypisElement.appendChild(smazatButton); this.vypisElement.innerHtml += "<br>"; } }
Všimnite si použitie potvrdzujúceho dialógu confirm()
,
odstránenie záznamu je určite akcia, ktorú nechceme urobiť omylom
Možno by vás napadlo vložiť tlačidlo rovno do HTML kódu
ako text a priradiť mu do dátumu atribútu index záznamu v poli, ktorý má
zmazať. Takáto tlačidla by sa potom niekde všetky vybrala a obslúžila tak,
aby z poľa zmazala prvok pod daným indexom. Problém by však nastal, keby sme
diár otvorili vo viacerých záložkách naraz. Keď by sme v jednej záložke
zmazali nejakú položku a druhú záložku neobnovili, táto položka by tu
stále bola, ale v localStorage
by pod týmto indexom už bola
položka iná. Mohli by sme tak na prvotne záložke nechtiac zmazať inú
úlohu. Preto budeme všetku manipuláciu s položkami vždy robiť
priamo pomocou anonymných funkcií, kam túto jednu konkrétnu položku
odovzdáme.
Ak krútite hlavou nad kódom odstraňujúcim položku:
this.zaznamy = this.zaznamy.filter(z => z !== zaznam);
Je to v súčasnej dobe žiaľ najjednoduchší spôsob, ako v JavaScripte zmazať prvok v poli, ktorého index nepoznáme a nechceme ho zbytočne zisťovať. Kód prefiltrujú dané pole tak, že v ňom zostanú len záznamy, ktoré sa nerovnajú záznamu, ktorý chceme odstrániť.
Pokračovať budeme zas nabudúce, v lekcii Kvíz - Dátové typy a dátové úložiská v JavaScripte , kedy pridáme tlačidlo na splnenie úlohy, validáciu dáta a diár dokončíme pridaním jednoduchých CSS štýlov
V nasledujúcom kvíze, Kvíz - Dátové typy a dátové úložiská v JavaScripte, si vyskúšame nadobudnuté skúsenosti z predchádzajúcich lekcií.