NOVINKA: Začni v IT jako webmaster s komplexním akreditovaným online kurzem Tvůrce WWW stránek. Zjisti více:
NOVINKA: Staň se datovým analytikem od 0 Kč a získej jistotu práce, lepší plat a nové kariérní možnosti. Více informací:

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:

Tvoje stránka
localhost

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:

Tvoje stránka
localhost

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

 

Předchozí článek
OOP diář v JavaScriptu - Ukládání, řazení, seskupování
Všechny články v sekci
Objektově orientované programování v JavaScriptu
Přeskočit článek
(nedoporučujeme)
Dokončení objektového diáře v JavaScriptu
Článek pro vás napsal Šimon Raichl
Avatar
Uživatelské hodnocení:
255 hlasů
Autor se věnuje především vývoji v JavaScriptu
Aktivity