# Vytvoření bankovní aplikace, část 1: HTML šablony a trasy ve webové aplikaci
## Kvíz před lekcí
[Kvíz před lekcí](https://ff-quizzes.netlify.app/web/quiz/41)
### Úvod
Od vzniku JavaScriptu v prohlížečích se webové stránky stávají interaktivnějšími a složitějšími než kdy dříve. Webové technologie se nyní běžně používají k vytváření plně funkčních aplikací, které běží přímo v prohlížeči, a nazýváme je [webové aplikace](https://en.wikipedia.org/wiki/Web_application). Protože jsou webové aplikace vysoce interaktivní, uživatelé nechtějí čekat na úplné načtení stránky pokaždé, když provedou nějakou akci. Proto se používá JavaScript k přímé aktualizaci HTML pomocí DOM, aby se zajistil plynulejší uživatelský zážitek.
V této lekci položíme základy pro vytvoření bankovní webové aplikace, přičemž použijeme HTML šablony k vytvoření více obrazovek, které lze zobrazit a aktualizovat bez nutnosti znovu načítat celou HTML stránku.
### Předpoklady
Pro testování webové aplikace, kterou v této lekci vytvoříme, potřebujete lokální webový server. Pokud ho nemáte, můžete si nainstalovat [Node.js](https://nodejs.org) a použít příkaz `npx lite-server` ve složce svého projektu. Tím vytvoříte lokální webový server a otevřete svou aplikaci v prohlížeči.
### Příprava
Na svém počítači vytvořte složku nazvanou `bank` a uvnitř ní soubor `index.html`. Začneme s tímto HTML [základem](https://en.wikipedia.org/wiki/Boilerplate_code):
```html
Bank App
```
---
## HTML šablony
Pokud chcete vytvořit více obrazovek pro webovou stránku, jedním řešením by bylo vytvořit jeden HTML soubor pro každou obrazovku, kterou chcete zobrazit. Toto řešení však přináší určité nevýhody:
- Při přepínání obrazovek musíte znovu načíst celé HTML, což může být pomalé.
- Je obtížné sdílet data mezi různými obrazovkami.
Dalším přístupem je mít pouze jeden HTML soubor a definovat více [HTML šablon](https://developer.mozilla.org/docs/Web/HTML/Element/template) pomocí elementu ``. Šablona je znovupoužitelný HTML blok, který není prohlížečem zobrazen a musí být instancován za běhu pomocí JavaScriptu.
### Úkol
Vytvoříme bankovní aplikaci se dvěma obrazovkami: přihlašovací stránkou a dashboardem. Nejprve přidáme do těla HTML prvek zástupce, který použijeme k instancování různých obrazovek naší aplikace:
```html
Loading...
```
Dáváme mu atribut `id`, aby bylo později snazší ho najít pomocí JavaScriptu.
> Tip: protože obsah tohoto prvku bude nahrazen, můžeme do něj vložit zprávu o načítání nebo indikátor, který se zobrazí, zatímco se aplikace načítá.
Dále přidáme pod tento prvek HTML šablonu pro přihlašovací stránku. Prozatím do ní vložíme pouze nadpis a sekci obsahující odkaz, který použijeme k navigaci.
```html
Bank App
```
Poté přidáme další HTML šablonu pro stránku dashboardu. Tato stránka bude obsahovat různé sekce:
- Hlavičku s nadpisem a odkazem pro odhlášení
- Aktuální zůstatek na bankovním účtu
- Seznam transakcí, zobrazený v tabulce
```html
```
> Tip: při vytváření HTML šablon, pokud chcete vidět, jak budou vypadat, můžete zakomentovat řádky `` a `` tím, že je obklopíte ``.
✅ Proč myslíte, že používáme atributy `id` na šablonách? Mohli bychom použít něco jiného, například třídy?
## Zobrazení šablon pomocí JavaScriptu
Pokud zkusíte aktuální HTML soubor v prohlížeči, uvidíte, že se zasekne na zobrazení `Loading...`. To je proto, že musíme přidat nějaký JavaScriptový kód, který instancuje a zobrazí HTML šablony.
Instancování šablony se obvykle provádí ve 3 krocích:
1. Získání elementu šablony v DOM, například pomocí [`document.getElementById`](https://developer.mozilla.org/docs/Web/API/Document/getElementById).
2. Klonování elementu šablony pomocí [`cloneNode`](https://developer.mozilla.org/docs/Web/API/Node/cloneNode).
3. Připojení k DOM pod viditelný prvek, například pomocí [`appendChild`](https://developer.mozilla.org/docs/Web/API/Node/appendChild).
✅ Proč musíme šablonu klonovat, než ji připojíme k DOM? Co si myslíte, že by se stalo, kdybychom tento krok přeskočili?
### Úkol
Vytvořte nový soubor nazvaný `app.js` ve složce svého projektu a importujte tento soubor do sekce `` svého HTML:
```html
```
Nyní v `app.js` vytvoříme novou funkci `updateRoute`:
```js
function updateRoute(templateId) {
const template = document.getElementById(templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
```
To, co zde děláme, jsou přesně 3 kroky popsané výše. Instancujeme šablonu s `id` `templateId` a vložíme její klonovaný obsah do našeho zástupce aplikace. Všimněte si, že musíme použít `cloneNode(true)`, abychom zkopírovali celý podstrom šablony.
Nyní zavolejte tuto funkci s jednou ze šablon a podívejte se na výsledek.
```js
updateRoute('login');
```
✅ Jaký je účel tohoto kódu `app.innerHTML = '';`? Co se stane bez něj?
## Vytváření tras
Když mluvíme o webové aplikaci, nazýváme *trasování* záměr mapovat **URL** na konkrétní obrazovky, které by měly být zobrazeny. Na webové stránce s více HTML soubory se to děje automaticky, protože cesty k souborům se odrážejí v URL. Například s těmito soubory ve složce vašeho projektu:
```
mywebsite/index.html
mywebsite/login.html
mywebsite/admin/index.html
```
Pokud vytvoříte webový server s `mywebsite` jako kořen, mapování URL bude:
```
https://site.com --> mywebsite/index.html
https://site.com/login.html --> mywebsite/login.html
https://site.com/admin/ --> mywebsite/admin/index.html
```
Nicméně pro naši webovou aplikaci používáme jeden HTML soubor obsahující všechny obrazovky, takže nám toto výchozí chování nepomůže. Musíme tuto mapu vytvořit ručně a aktualizovat zobrazenou šablonu pomocí JavaScriptu.
### Úkol
Použijeme jednoduchý objekt k implementaci [mapy](https://en.wikipedia.org/wiki/Associative_array) mezi cestami URL a našimi šablonami. Přidejte tento objekt na začátek svého souboru `app.js`.
```js
const routes = {
'/login': { templateId: 'login' },
'/dashboard': { templateId: 'dashboard' },
};
```
Nyní trochu upravíme funkci `updateRoute`. Místo toho, abychom přímo předávali `templateId` jako argument, chceme ho získat nejprve pohledem na aktuální URL a poté použít naši mapu k získání odpovídající hodnoty `templateId`. Můžeme použít [`window.location.pathname`](https://developer.mozilla.org/docs/Web/API/Location/pathname) k získání pouze části cesty z URL.
```js
function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
const template = document.getElementById(route.templateId);
const view = template.content.cloneNode(true);
const app = document.getElementById('app');
app.innerHTML = '';
app.appendChild(view);
}
```
Zde jsme mapovali trasy, které jsme deklarovali, na odpovídající šablonu. Můžete vyzkoušet, že to funguje správně, změnou URL ručně ve svém prohlížeči.
✅ Co se stane, když zadáte neznámou cestu do URL? Jak bychom to mohli vyřešit?
## Přidání navigace
Dalším krokem pro naši aplikaci je přidání možnosti navigace mezi stránkami bez nutnosti ručně měnit URL. To zahrnuje dvě věci:
1. Aktualizaci aktuálního URL
2. Aktualizaci zobrazené šablony na základě nového URL
Druhou část jsme již vyřešili pomocí funkce `updateRoute`, takže musíme zjistit, jak aktualizovat aktuální URL.
Budeme muset použít JavaScript a konkrétně [`history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState), který umožňuje aktualizovat URL a vytvořit nový záznam v historii prohlížení, aniž by se znovu načítalo HTML.
> Poznámka: Zatímco HTML prvek kotvy [``](https://developer.mozilla.org/docs/Web/HTML/Element/a) může být použit samostatně k vytvoření hypertextových odkazů na různé URL, ve výchozím nastavení způsobí, že prohlížeč znovu načte HTML. Je nutné tomuto chování zabránit při manipulaci s trasováním pomocí vlastního JavaScriptu, použitím funkce preventDefault() na události kliknutí.
### Úkol
Vytvořme novou funkci, kterou můžeme použít k navigaci v naší aplikaci:
```js
function navigate(path) {
window.history.pushState({}, path, path);
updateRoute();
}
```
Tato metoda nejprve aktualizuje aktuální URL na základě zadané cesty a poté aktualizuje šablonu. Vlastnost `window.location.origin` vrací kořen URL, což nám umožňuje rekonstruovat kompletní URL ze zadané cesty.
Nyní, když máme tuto funkci, můžeme se postarat o problém, který máme, pokud cesta neodpovídá žádné definované trase. Upravením funkce `updateRoute` přidáme záložní trasu na jednu z existujících tras, pokud nemůžeme najít shodu.
```js
function updateRoute() {
const path = window.location.pathname;
const route = routes[path];
if (!route) {
return navigate('/login');
}
...
```
Pokud nelze najít trasu, nyní přesměrujeme na stránku `login`.
Nyní vytvořme funkci pro získání URL při kliknutí na odkaz a zabránění výchozímu chování prohlížeče při kliknutí na odkaz:
```js
function onLinkClick(event) {
event.preventDefault();
navigate(event.target.href);
}
```
Dokončeme navigační systém přidáním vazeb na naše odkazy *Login* a *Logout* v HTML.
```html
Login
...
Logout
```
Objekt `event` výše zachytí událost `click` a předá ji naší funkci `onLinkClick`.
Pomocí atributu [`onclick`](https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/onclick) připojte událost `click` k JavaScriptovému kódu, zde volání funkce `navigate()`.
Zkuste kliknout na tyto odkazy, nyní byste měli být schopni navigovat mezi různými obrazovkami své aplikace.
✅ Metoda `history.pushState` je součástí standardu HTML5 a je implementována [ve všech moderních prohlížečích](https://caniuse.com/?search=pushState). Pokud vytváříte webovou aplikaci pro starší prohlížeče, existuje trik, který můžete použít místo této API: použití [hash (`#`)](https://en.wikipedia.org/wiki/URI_fragment) před cestou vám umožní implementovat trasování, které funguje s běžnou navigací kotvy a nenačítá stránku znovu, protože jeho účelem bylo vytvořit interní odkazy na stránce.
## Řešení tlačítek zpět a vpřed v prohlížeči
Použití `history.pushState` vytváří nové záznamy v historii navigace prohlížeče. Můžete to zkontrolovat podržením *tlačítka zpět* svého prohlížeče, mělo by zobrazit něco jako toto:

Pokud zkusíte několikrát kliknout na tlačítko zpět, uvidíte, že se aktuální URL mění a historie se aktualizuje, ale stále se zobrazuje stejná šablona.
To je proto, že aplikace neví, že je potřeba zavolat `updateRoute()` pokaždé, když se historie změní. Pokud se podíváte na [dokumentaci `history.pushState`](https://developer.mozilla.org/docs/Web/API/History/pushState), můžete vidět, že pokud se stav změní - což znamená, že jsme se přesunuli na jiné URL - je spuštěna událost [`popstate`](https://developer.mozilla.org/docs/Web/API/Window/popstate_event). Použijeme to k vyřešení tohoto problému.
### Úkol
Abychom zajistili, že zobrazená šablona bude aktualizována, když se historie prohlížeče změní, připojíme novou funkci, která volá `updateRoute()`. Uděláme to na konci našeho souboru `app.js`:
```js
window.onpopstate = () => updateRoute();
updateRoute();
```
> Poznámka: zde jsme použili [arrow function](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Functions/Arrow_functions) k deklaraci našeho obslužného programu události `popstate` pro stručnost, ale běžná funkce by fungovala stejně.
Zde je video o arrow funkcích:
[](https://youtube.com/watch?v=OP6eEbOj2sc "Arrow Functions")
> 🎥 Klikněte na obrázek výše pro video o arrow funkcích.
Nyní zkuste použít tlačítka zpět a vpřed svého prohlížeče a zkontrolujte, že zobrazená trasa je tentokrát správně aktualizována.
---
## 🚀 Výzva
Přidejte novou šablonu a trasu pro třetí stránku, která zobrazuje kredity této aplikace.
## Kvíz po lekci
[Kvíz po lekci](https://ff-quizzes.netlify.app/web/quiz/42)
## Přehled a samostudium
Trasování je jednou z překvapivě složitých částí vývoje webu, zejména když se web přesouvá od chování při obnovování stránky k obnovování stránky v aplikacích typu Single Page Application. Přečtěte si něco o [tom, jak služba Azure Static Web App](https://docs.microsoft.com/azure/static-web-apps/routes/?WT.mc_id=academic-77807-sagibbon) řeší trasování. Dokážete vysvětlit, proč jsou některá rozhodnutí popsaná v tomto dokumentu nezbytná?
## Zadání
[Zlepšete trasování](assignment.md)
---
**Prohlášení**:
Tento dokument byl přeložen pomocí služby pro automatický překlad [Co-op Translator](https://github.com/Azure/co-op-translator). I když se snažíme o přesnost, mějte prosím na paměti, že automatické překlady mohou obsahovat chyby nebo nepřesnosti. Původní dokument v jeho původním jazyce by měl být považován za autoritativní zdroj. Pro důležité informace doporučujeme profesionální lidský překlad. Neodpovídáme za žádné nedorozumění nebo nesprávné interpretace vyplývající z použití tohoto překladu.