Lekce 9 - OOP diář v JavaScriptu - Formátování a mazání záznamů
V minulé lekci, OOP diář v JavaScriptu - Ukládání, řazení, seskupování, jsme do našeho OOP diáře přidali ukládání, řazení a seskupování.
V dnešním tutoriálu objektově orientovaného programování v JavaScriptu se budeme věnovat dalším vylepšením našeho diáře. Zaměříme se na formátování data a stavu splnění úkolu a přidáme mazání jednotlivých záznamů pomocí tlačítka.
Formátování data a splněnosti
Jelikož aktuální formát data a informace o splnění/nesplnění úkolu
nejsou v našem výpisu ideální, upravíme výpis do "lidštější" podoby

Metoda #naformatujDatum()
Pamatujeme si na vlastnost #jazyk, kterou můžeme ovlivnit
předáním parametru konstruktoru? Nyní ji využijeme k lepšímu
formátování data. Pro naformátování data si do naší třídy
Diar přidáme novou privátní metodu
#naformatujDatum(), třeba hned pod metodu
#seradZaznamy():
#naformatujDatum(datum) { const datumDate = new Date(datum); return datumDate.toLocaleDateString(this.#jazyk, { weekday: "long", day: "numeric", month: "short", year: "numeric" }); }
Jak můžeme vidět, metoda je vcelku jednoduchá. Parametrem jí
předáváme datum, ze kterého vytváříme instanci třídy Date.
Následně používáme její metodu toLocaleDateString() pro
samotné formátování. Tato metoda přijímá jako první parametr jazyk, ve
kterém má datum naformátovat. My zde předáváme hodnotu naší vlastnosti
#jazyk. Jako druhý parametr pak metoda
toLocaleDateString() přijímá formátovací objekt. Jeho
vlastnosti udávají, jak přesně se má datum vypsat.
Všechny možnosti formátovacího objektu najdeme popsány v dokumentaci.
Úprava výpisu
Nyní se již přesuneme do metody vypisZaznamy(), kde
použijeme právě vytvořenou metodu #naformatujDatum() a
zároveň si vylepšíme výpis splněnosti úkolů:
vypisZaznamy() { this.#seradZaznamy(); this.#vypisElement.innerHTML = ""; let posledniDatum = null; for (const zaznam of this.#zaznamy) { if (zaznam.datum !== posledniDatum) { const naformatovaneDatum = this.#naformatujDatum(zaznam.datum); this.#vypisElement.insertAdjacentHTML("beforeend", `<h2>${naformatovaneDatum}</h2>`); } posledniDatum = zaznam.datum; this.#vypisElement.insertAdjacentHTML( "beforeend", `<h3>${zaznam.nazev}</h3>úkol ${!zaznam.splneno ? "ne" : ""}splněn<hr>`); } }
Datum záznamu nejprve formátujeme pomocí metody
#naformatujDatum() a až poté jej vypisujeme.
U splněnosti úkolu pak používáme jednoduchý ternární operátor,
který známe z kurzu základů JavaScriptu z lekce Podmínky
v JavaScriptu potřetí. Podle nesplněnosti přidáváme ke slovu
"splněn" buď řetězec "ne", anebo prázdný
řetězec.
Výsledek
Výpis nyní vypadá takto:
Mazání záznamů
Výpis záznamů již vypadá celkem slušně. Hodilo by se však mít i možnost záznamy mazat nebo označit jako splněné. Začneme s implementací mazání.
Uložení záznamů
Jelikož po smazání bude nutné záznamy znovu uložit, vyčleňme si
uložení záznamů z metody #nastavUdalosti() do samostatné
metody #ulozZaznamy(). Pod metodu #naformatujDatum()
vložíme tento kód:
#ulozZaznamy() { const jsonZaznamy = JSON.stringify(this.#zaznamy); localStorage.setItem("zaznamy", jsonZaznamy); }
V metodě #nastavUdalosti() nyní metodu
#ulozZaznamy() zavoláme:
#nastavUdalosti() { this.#potvrditButton.onclick = () => { const zaznam = new Zaznam(this.#nazevInput.value, this.#datumInput.value); this.#zaznamy.push(zaznam); this.#ulozZaznamy(); this.vypisZaznamy(); }; }
Mazací tlačítko
Nyní se již zaměříme na samotné mazání. Ke každému záznamu v
metodě vypisZaznamy() vygenerujeme tlačítko na jeho smazání.
To vytvoříme jako nový element <button> pomocí metody
document.createElement() a do elementu pro výpis záznamů jej
vložíme pomocí metody appendChild(). Metoda
vypisZaznamy() bude po přidání mazacího tlačítka vypadat
takto:
vypisZaznamy() { this.#seradZaznamy(); this.#vypisElement.innerHTML = ""; let posledniDatum = null; for (const zaznam of this.#zaznamy) { if (zaznam.datum !== posledniDatum) { const naformatovaneDatum = this.#naformatujDatum(zaznam.datum); this.#vypisElement.insertAdjacentHTML("beforeend", `<h2>${naformatovaneDatum}</h2>`); } posledniDatum = zaznam.datum; this.#vypisElement.insertAdjacentHTML( "beforeend", `<h3>${zaznam.nazev}</h3>úkol ${!zaznam.splneno ? "ne" : ""}splněn<br>`); // Následující kód jsme přidali const smazatButton = document.createElement("button"); smazatButton.onclick = () => { if (confirm("Opravdu si přejete odstranit úkol?")) { this.#zaznamy = this.#zaznamy.filter(z => z !== zaznam); this.#ulozZaznamy(); this.vypisZaznamy(); } }; smazatButton.innerText = "Smazat úkol"; this.#vypisElement.appendChild(smazatButton); this.#vypisElement.insertAdjacentHTML("beforeend", "<hr>"); } }
Tlačítku rovněž přiřazujeme obsluhu události kliknutí, ve které
daný záznam odstraňujeme z našeho pole #zaznamy. Změny poté
ukládáme do localStorage a všechny záznamy znovu
vypisujeme.
Všimněme si také použití potvrzujícího dialogu confirm(),
odstranění záznamu je určitě akce, kterou nechceme udělat omylem 
U výpisu obsahu záznamu jsme ještě změnili element
<hr> oddělující jednotlivé záznamy na element
<br>. Element <hr> pak přidáváme až
pod mazací tlačítko.
Alternativní řešení?
Možná by nás napadlo vložit tlačítko rovnou do HTML kódu jako text a přiřadit mu do data-atributu index záznamu v poli, který má smazat. Taková tlačítka by se poté někde všechna vybrala a obsloužila tak, aby z pole smazala prvek pod daným indexem. Problém by ovšem nastal, kdybychom diář otevřeli ve více záložkách najednou.
Pokud bychom pak v jedné záložce smazali nějakou položku a druhou
záložku neobnovili, tato položka by zde stále byla, ale v
localStorage by pod tímto indexem již byla položka jiná. Mohli
bychom tak na neobnovené záložce nechtěně smazat jiný záznam.
Abychom zajistili, že obsluha kliknutí na tlačítko bude pracovat se správným záznamem, přiřadíme vždy každému tlačítku samostatnou anonymní funkci, kam tento jeden konkrétní záznam předáme.
Mazání z pole
Je možné, že nás ještě zarazil kód odebírající položku z pole:
this.zaznamy = this.zaznamy.filter(z => z !== zaznam);
Je to v současné době bohužel nejjednodušší způsob, jak v JavaScriptu smazat prvek v poli, jehož index neznáme a nechceme jej zbytečně zjišťovat. Kód profiltruje dané pole tak, že v něm zůstanou jen záznamy, které se nerovnají záznamu, který chceme odstranit.
Výsledek
Zkusme si aplikaci nyní spustit a smazat nějaký záznam:
Vše funguje, jak má 
Vyčlenění logiky vytvoření tlačítka
Určitě jsme si ale všimli, že po přidání kódu pro vytvoření
mazacího tlačítka naše metoda vypisZaznamy() celkem dost
narostla a stává se nepřehlednou. V tomto okamžiku bychom
se proto měli zamyslet nad tím, zda toho metoda vypisZaznamy()
náhodou nedělá více, než by měla, a zda bychom případně neměli
vyčlenit část kódu do samostatné metody.
Připomeňme si, že zodpovědností metody vypisZaznamy() je
vypisování záznamů na stránku. Kód pro vytváření a
obsluhu mazacího tlačítka je v ní tedy z tohoto pohledu navíc. Proto
právě tento kód vyčleníme do samostatné metody a metoda
vypisZaznamy() se pak bude soustředit jen na samotný výpis.
Princip, kdy je každá metoda zodpovědná za jednu jasně definovanou věc, se označuje jako Single Responsibility Principle (SRP). Jde o jedno z nejdůležitějších pravidel kvalitního objektového návrhu. Více se o něm dočteme v lekci Best practices pro vývoj softwaru - Základní praktiky.
Přidejme si do naší třídy Diar pomocnou metodu
#vytvorMazaciTlacitko(), jejímž jediným účelem bude vytvořit
mazací tlačítko a obsloužit jeho událost kliknutí:
#vytvorMazaciTlacitko(zaznam) { const smazatButton = document.createElement("button"); smazatButton.onclick = () => { if (confirm("Opravdu si přejete odstranit úkol?")) { this.#zaznamy = this.#zaznamy.filter(z => z !== zaznam); this.#ulozZaznamy(); this.vypisZaznamy(); } }; smazatButton.innerText = "Smazat úkol"; return smazatButton; }
Metoda přijímá parametrem pouze záznam, pro nějž se má tlačítko vytvořit. Samotné tlačítko nakonec vrací.
Nyní se ještě jednou vrátíme k metodě vypisZaznamy() a
upravíme ji tak, aby využívala novou metodu
#vytvorMazaciTlacitko():
vypisZaznamy() { this.#seradZaznamy(); this.#vypisElement.innerHTML = ""; let posledniDatum = null; for (const zaznam of this.#zaznamy) { if (zaznam.datum !== posledniDatum) { const naformatovaneDatum = this.#naformatujDatum(zaznam.datum); this.#vypisElement.insertAdjacentHTML("beforeend", `<h2>${naformatovaneDatum}</h2>`); } posledniDatum = zaznam.datum; this.#vypisElement.insertAdjacentHTML( "beforeend", `<h3>${zaznam.nazev}</h3>úkol ${!zaznam.splneno ? "ne" : ""}splněn<br>`); const smazatButton = this.#vytvorMazaciTlacitko(zaznam); this.#vypisElement.appendChild(smazatButton); this.#vypisElement.insertAdjacentHTML("beforeend", "<hr>"); } }
Tím máme mazání záznamů hotové.
V další lekci, Dokončení objektového diáře v JavaScriptu, do našeho diáře přidáme tlačítko na splnění
úkolu, validaci data a jednoduché CSS styly 
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 304x (3.18 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript

