Triko zdarma Triko zdarma
Spouštíme individuální výuku programování! Zaváděcí slevy 799 Kč 599 Kč/60 minut se zkušeným lektorem! Výuka osobně Praha a okolí nebo po Skype celá ČR. O termíny a slevu si pište na [email protected].
Extra 10 % bodů navíc a tričko zdarma při zadání kódu "TRIKO10"

Lekce 7 - Vylepšení objektového diáře v JavaScriptu

JavaScript Objektově orientované programování Vylepšení objektového diáře v JavaScriptu

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci, Objekty, JSON a vylepšení diáře v JavaScriptu, jsme si vylepšili náš diář o ukládání do localStorage. Dnes doplníme řazení záznamů podle data, seskupování záznamů se stejným datem a odstranění záznamů.

Výpis záznamů

Prvně si upravíme výpis našich záznamů. Bylo by vhodné záznamy seřadit dle data a seskupit záznamy ve stejný den, které vypíšeme pod sebou v jednom bloku.

Řazení

Nejdříve si naimplementujeme metodu na řazení záznamů. K tomu použijeme metodu sort() na poli. Ta dokáže pole seřadit postupným porovnáváním dvojic prvků. Jako parametr přijímá porovnávací funkci, která definuje jakým způsobem se mají 2 prvky v poli porovnat. Naše metoda k seřazení záznamů v poli podle data bude ve třídě Diar vypadat následovně:

seradZaznamy() {
    this.zaznamy.sort(function (zaznam1, zaznam2) {
        return (new Date(zaznam1.datum) - new Date(zaznam2.datum));
    });
}

Funkce porovnává data dvou záznamů, která si naparsujeme na datum pomocí konstruktoru objektu Date. Ze záznamu samozřejmě vybereme vlastnost datum. Porovnání dvou datumů provedeme jednoduše pomocí operátoru -, čímž se vrátí jejich rozdíl v milisekundách. Pokud bude první datum až po druhém, vrátí se záporné číslo. Pokud budou stejné, vrátí se 0. Jinak se vrátí kladné číslo. Právě kladné, záporné nebo nulové číslo metoda sort() pro svou práci potřebuje a tím zjistí, zda je datum větší, menší nebo rovné.

Metodu budeme volat před každým výpisem záznamů:

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}`;
    }
}

Seřazení bychom mohli také volat po přidání záznamu a po jejich načtení, aby se nemuselo volat při každém výpisu. Nevýhodou tohoto řešení by ovšem bylo, že bychom na něj mohli zapomenou při jiné manipulaci se záznamy.

Pokud nyní diář spustíme a máme v localStorage již nějaká data, vypíší se nám již seřazená podle datumu.

Seskupování

Nyní výpis záznamů dokončíme. Budeme tedy vypisovat datum a k tomuto datumu vždy všechny záznamy v daný den. Náš cyklus lehce 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 proměnné posledniDatum přiřadíme vlastnost datum z předchozího záznamu. Protože při prvním průběhu cyklu poslední záznam ještě není, nastavíme proměnnou prvně na null. Datum současného záznamu poté vypíšeme jen pokud se liší od předchozího. Tak se záznamy ve stejný den budou seskupovat. Nyní máme úkoly vypsané hezky úkoly s jedním datem pod sebou a seřazené:

Your page
localhost

Formátování data a splněnosti

Jelikož formát data a splněnost úkolu také není ideální, je třeba tyto výpisy upravit do "lidštější" podoby :) Pamatujete na vlastnost jazyk, kterou můžeme ovlivnit v konstruktoru? Nyní jí využijeme k lepšímu výpisu data 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 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>`;
    }
}

Vytvoříme instanci objektu Date z našeho data a použijeme její metodu toLocaleString(), kam předáme jako první parametr vlastnost jazyk naší třídy a jako druhý parametr formátovací objekt, jehož vlastnosti udávají jak přesně se má datum vypsat.

U splněnosti úkolu jsme použili jednoduchý ternární operátor, podle nesplněnosti přidáme "ne" nebo prázdný string.

Výsledek nyní vypadá takto:

Your page
localhost

Mazání záznamů

Budeme pokračovat základní interakcí s našimi úkoly, budeme je moci mazat, nebo je označit jako splněné. Začneme mazáním.

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():

ulozZaznamy() {
    localStorage.setItem("zaznamy", JSON.stringify(this.zaznamy));
}

V metodě nastavUdalosti() nyní metodu 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čítko

Ke každému záznamu vygenerujeme tlačítko na jeho odstranění. To vytvoříme jako nový element <button> pomocí metody document.createElement() a do <div>u s výpisem záznamů jej vložíme pomocí appendChild(). Tlačítku rovněž přidáme událost reakce na kliknutí, kdy daný záznam odstraníme z pole, záznamy takto přeuložíme do localStorage a znovu vypíšeme. 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 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šimněte si použití potvrzujícího dialogu confirm(), odstranění záznamu je určitě akce, kterou nechceme udělat omylem :)

Možná by vá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. Když bychom 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ý úkol. Proto budeme veškerou manipulaci s položkami vždy dělat přímo pomocí anonymních funkcí, kam tuto jednu konkrétní položku předáme.

Pokud kroutíte hlavou nad kódem odstraňujícím položku:

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.

Pokračovat budeme zas příště, v lekci Dokončení objektového diáře v JavaScriptu, kdy přidáme tlačítko na splnění úkolu, validaci data a diář dokončíme přidáním jednoduchých CSS stylů :)


 

 

Článek pro vás napsal Šimon Raichl
Avatar
Jak se ti líbí článek?
Ještě nikdo nehodnotil, buď první!
Autor se věnuje hlavně tvorbě všemožných věcí v JS
Aktivity (4)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!