Lekce 5 - Tvorba OOP diáře v JavaScriptu
V minulé lekci, Referenční a hodnotové datové typy v JavaScriptu, jsme si vysvětlili rozdíly mezi hodnotovými a referenčními datovými typy. Již víme, že když uložíme instanci třídy do nějaké proměnné, je v ní ve skutečnosti uložena reference (odkaz) na tuto instanci. Můžeme tak používat jednu instanci z několika proměnných nebo ji jednoduše předávat, aniž by se zkopírovala.
Jak jsem slíbil, v dnešním tutoriálu objektově orientovaného programování v JavaScriptu začneme programovat elektronický diář. Využijeme zde také znalosti z minula.
Příprava
Nejdříve se ale zamyslíme nad tím, co vše budeme potřebovat.
Vytvoříme si jednoduchou stránku, index.html
, s formulářem na
přidání záznamu nahoře a výpisem záznamů dole. Do nějakého kontejneru,
např. do elementu <div>
, si vložíme dva
inputy. Budou typu "text"
a "date"
na název
úkolu a jeho datum. Nakonec ještě přidáme tlačítko na potvrzení. Níže
přidáme druhý <div>
na seznam úkolů.
Co se týče JavaScriptu, můžeme si vytvořit složku js/
a v
ní tři skripty: Diar.js
, Zaznam.js
a
obsluha.js
. Rovnou je v naší stránce i naodkazujeme.
Soubor index.html
by tedy mohl vypadat takto:
<!DOCTYPE html> <html lang="cs-cz"> <head> <meta charset="UTF-8"> <title>Diář</title> </head> <body> <h1>Diář</h1> <div> <input type="text" id="nazev" placeholder="Vyplňte název úkolu"><br> <input type="date" id="datum" placeholder="Vyplňte datum"><br> <button id="potvrdit">Uložit úkol</button> </div> <div id="seznam-ukolu"> </div> <script src="js/Diar.js"></script> <script src="js/Zaznam.js"></script> <script src="js/obsluha.js"></script> </body> </html>
Stránka by mohla vypadat takto:
Vzhled nyní nebudeme příliš řešit.
Záznam
Začneme se souborem Zaznam.js
. Jak asi tušíte, soubor bude
obsahovat třídu reprezentující jeden záznam v našem diáři. Můžeme jí
tedy dát různé vlastnosti, které při vytvoření a po vytvoření záznamu
budeme moci měnit. Zatím to může být název, datum a zda byl úkol splněn.
První dva parametry nastavíme konstruktorem a co se týká splněnosti úkolu,
tak budeme předpokládat, že je po vytvoření vždy nesplněný:
class Zaznam { constructor(nazev, datum) { this.nazev = nazev; this.datum = datum; this.splneno = false; } }
Diář
Přesuňme se nyní k samotnému diáři.
Konstruktor
V souboru Diar.js
si vytvoříme třídu Diar
s
konstruktorem a v něm nadefinujeme několik vlastností:
zaznamy
- Záznamy diáře vytvoříme jako prázdné pole.jazyk
- Jazyk výpisu data (datumů) záznamů se nám může v budoucnu hodit, jelikož různé jazyky mají různé formáty. Např. české datum vypadá jinak než anglické. Pro nastavení jazyka přidáme do konstruktoru parametr. Protože budeme chtít většinou české prostředí, definujeme mu výchozí hodnotu"cs-CZ"
, která se použije pokud parametr nezadáme.
Třída by zatím mohla vypadat takto:
class Diar { constructor(jazyk = "cs-CZ") { this.zaznamy = []; this.jazyk = jazyk; } }
Vybírání elementů na stránce
Ve třídě budeme potřebovat pracovat s DOM elementy na stránce. Možná jste se již setkali s principem oddělení práce s logikou od práce s uživatelským rozhraním. Tohoto základního programátorského pravidla využívá např. architektura MVC.
V javascriptových frameworcích, ke kterým se dostanete po tomto kurzu, je architektura aplikace postavená tak, aby se nemíchal kód vybírající elementy na stránce s dalším kódem aplikace. Bylo by to totiž velmi nepřehledné.
My si v základním OOP kurzu nebudeme vytvářet žádnou komplikovanou architekturu, ale budeme se snažit umístit vybírání elementů ze stránky na jedno jediné místo ve třídě. Tímto místem bude právě konstruktor. Vytvoříme si zde několik dalších vlastností a do nich uložíme elementy ze stránky, které budeme ve třídě dále potřebovat:
nazevInput
- Input element s názvem nově přidávaného záznamu.datumInput
- Input element s datem nově přidávaného záznamu.potvrditButton
- Ukládací tlačítko.vypisElement
- Element pro výpis záznamů uložených v diáři.
Konstruktor bude nyní vypadat takto:
constructor(jazyk = "cs-CZ") { this.zaznamy = []; this.jazyk = jazyk; this.nazevInput = document.getElementById("nazev"); this.datumInput = document.getElementById("datum"); this.potvrditButton = document.getElementById("potvrdit"); this.vypisElement = document.getElementById("seznam-ukolu"); }
Nikam jinam ve třídě nebudeme dále vkládat žádný další výběr elementů, protože by to bylo velmi nepřehledné.
Metody
Přejděme k metodám diáře.
nastavUdalosti()
Aby náš konstruktor nebyl příliš dlouhý, vyčleníme nastavení obslužných událostí elementům na stránce do oddělené metody. V našem případě jde jen o obsluhu kliknutí na tlačítko.
Zatím si přidejme naivní implementaci metody pro obsluhu tlačítka, která nebude fungovat. Proč si vysvětlíme za okamžik:
nastavUdalosti() { this.potvrditButton.onclick = function() { // tento kód nebude fungovat const zaznam = new Zaznam(this.nazevInput.value, this.datumInput.value); this.zaznamy.push(zaznam); this.vypisZaznamy(); }; }
Metoda na událost onclick
tlačítka
this.potvrditButton
naváže obslužnou funkci. Zde je ještě vše
v pořádku. Uvnitř se vezmou hodnoty z inputů a na jejich základě se
vytvoří nový záznam. Tuto novou instanci vložíme do pole. Všechny
záznamy poté vypíšeme.
Co je tedy špatně? Pokud jste dávali v Základních konstrukcích JavaScriptu pozor, víte, že:
Při použití function
pro
obsluhu událostí elementů se mění kontext a klíčové
slovo this
následně ukazuje na element, který událost
způsobil. this
tedy přestane obsahovat instanci naší
třídy. Toto chování je chyba v návrhu jazyka JavaScript a
zabraňuje nám pracovat s instančními proměnnými a metodami v obsluze
událostí.
Arrow functions
Způsobů, jak tento problém obejít, je hned několik. My si zmíníme to
nejjednodušší řešení. K obsluze události použijeme tzv. arrow
function, což je "zkrácený" zápis funkce. Název vychází ze znaku
šipky (anglicky arrow), kterým se tyto funkce zapisují. Arrow
functions byly do JavaScriptu přidány až později a proto chybou změny
kontextu již netrpí. Přesněji ani žádný svůj kontext nemají a
klíčové slovo this
v nich obsahuje to, co v
něm bylo předtím, beze změny.
Arrow function do nějaké proměnné uložíme následujícím způsobem:
nazevFunkce = () => { // tělo funkce }
Pokud bychom chtěli funkci poslat i parametry, můžeme je psát do závorek, jak jsme zvyklí:
nazevFunkce = (parametr) => { // tělo funkce }
Pokud bychom chtěli poslat pouze jeden, můžeme závorky dokonce i vynechat:
nazevFunkce = parametr => { // tělo funkce }
Nyní tedy metodu nastavUdalosti()
opravíme, aby v obslužné
funkci fungovalo klíčové slovo this
. To totiž používáme k
přístupu k vlastnostem naší třídy:
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.vypisZaznamy(); }; }
Metodu zavoláme na konci konstruktoru:
this.nastavUdalosti();
vypisZaznamy()
V metodě pro výpis úkolů nás asi nic nepřekvapí, funguje velmi podobně jako výpis zaměstnanců, který jsme vytvářeli v předešlých lekcích:
vypisZaznamy() { 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}`; } }
Metoda smaže veškerý obsah z našeho elementu a vypíše tam postupně naše úkoly pomocí cyklu.
Obsluha
Nakonec musíme ještě v obsluha.js
vytvořit instanci třídy
Diar
a vypsat uložené záznamy. Ty tam ve chvíli vytvoření
diáře ještě nejsou, ale dále v kurzu je budeme načítat z lokálního
úložiště:
const diar = new Diar(); diar.vypisZaznamy();
Pokud nyní naší aplikaci spustíme v prohlížeči, bude vypadat takto:
V případě jakýchkoli problému si svůj kód porovnejte s funkčním řešením v příloze.
V následujícím cvičení, Řešené úlohy k 4.-5. lekci OOP v JavaScriptu, si procvičíme nabyté zkušenosti z předchozích lekcí.
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 785x (1.86 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript