V našem kurzu postupně směřujeme ke stále větším a složitějším aplikacím. Jakmile velikost aplikace přesáhne určitou mez, začne být výhodné ji rozsekat na menší kousky, jakési stavební bloky, ze kterých pak výslednou aplikaci poskládáme jako z kostek lega. Takovým stavebním blokům se často říká komponenty components . V této lekci si ukážeme, jak postavit jednoduchou komponentu a jak ji použít v naší aplikaci.

Šablony

K tomu, abychom mohli postavit naši první komponentu budeme nejdříve potřebovat větší flexibilitu pro tvorbu HTML pomocí JavaScriput. Do této chvíle jsme naše elementy plnili obsahem pomocí vlastnosti innerHTML a interpolace řetězců. Připomeňme si, jak jsme vytvořili element epizody v naší podcastové aplikaci.

const num = 123;
const title = 'Robot, který snědl koblihu';
const guest = 'Radovan Holátko';

const episodeElm = document.createElement('div');
episodeElm.className = 'episode';
episodeElm.innerHTML = `
  <div class="episode__num">${num}</div>
  <div class="episode__body">
    <div class="episode__title">${title}</div>
    <div class="episode__guest">${guest}</div>
  </div>
`;

Tento zápis má jednu velkou nevýhodu. Řetězec obsahující naše HTML lze vytvořit jedině ve chvíli, kdy už máme připravený obsah proměnných num, title a guest. Nemůžeme si jej proto nejdříve vytvořit a použít až později. Pokud proměnné num, title a guest ještě neexistují, následující kód vyhodí výjimku.

const episodeHTML = `
  <div class="episode__num">${num}</div>
  <div class="episode__body">
    <div class="episode__title">${title}</div>
    <div class="episode__guest">${guest}</div>
  </div>
`;

Z tohoto problému se dostaneme tak, že použijeme skutečnou šablonovací knihovnu.

Handlebars

Díky šablonám si můžeme náš řetězec s proměnnými připravit dopředu a použít jej až ve chvíli, kdy už víme, jakou mají tyto proměnné mít hodnotu. Šablonovacích knihoven existuje celá řada. My v tomto kurzu použijeme systém Handlebars. Abychom mohli Handlebars šablony použít na naší stránce, musíme do hlavičky stránky vložit kód této knihovny.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Šablony</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.6/handlebars.min.js"></script>
  </head>
  <body>
    <script src="index.js"></script>
  </body>
</html>

Pokud tuto stránku otevřeme v konzoli, můžeme si začít hrát se šablonami. Každá šablona je obyčejný řetězec, který ve dvojitých složených závorkách může obsahovat parametry šablony.

> const msgTemp = 'Film {{movie}} začne za {{time}} minut.';

Každou takovou šablonu musím takzvaně zkompilovat pomocí funkce Handlebars.compile. Tato funkce z našeho řetězce vytvoří novou funkci, kterou pak můžeme kdykoliv zavolat s libovolnými hodnotami. Hodnoty pro šablonu se specifikují jako objekt.

> const template = Handlebars.compile(msgTemp);
undefined
> template({ movie: 'Harry Potter', time: 20 })
'Film Harry Potter začne za 20 minut.'
> template({ movie: 'Román pro ženy', time: 5 })
'Film Román pro ženy začne za 5 minut.'

Takto můžeme pomocí naší připravené šablony vyrobit řetězec s libovolnými hodnotami parametrů. Takto si například můžeme dopředu vytvořit šablony pro epizodu podcastu a pak ji použít pro vytvoření našeho elementu.

const episodeTemplate = Handlebars.compile(`
  <div class="episode__num">{{num}}</div>
  <div class="episode__body">
    <div class="episode__title">{{title}}</div>
    <div class="episode__guest">{{guest}}</div>
  </div>
`);

const episodeElm = document.createElement('div');
episodeElm.className = 'episode';
episodeElm.innerHTML = episodeTemplate({
  num: 123,
  title: 'Robot, který snědl koblihu',
  guest: 'Radovan Holátko',
});

Cvičení - šablony

1

Šablony

pohodička

Stáhněte si připravenou stránku se vloženými Handlebars šablonami. Prohlédněte si kód v souboru index.js a otevřete stránku v prohlížeči.

  1. V konzoli zavolejte funkci movieTemplate s objektem movie1 a prohlédněte si výsledek.
  2. V souboru index.js vytvořte podle předchozího vzoru objekty movie2 a movie3 představující dva další filmy. V konzoli vyzkoušejte tyto objekty s vaší šablonou.
  3. Pomocí knihovny Handlebars vytvořte šablonu cinemaTemplate, která bude produkovat řetězce typu
    Kino Světozor, adresa: Vodičkova 41
    
    kde název kina a adresu specifikujete pomocí objektu s vlastnostmi name a address. Vytvořte tři objekty kin a pomocí šablony cinemaTemplate vypište do konzole následující řetězce.
    Kino Světozor, adresa: Vodičkova 41
    Kino Bio Oko, adresa: Františka Křížka 460/15
    Kino Aero, adresa: Biskupcova 31
    
2

Název kina

to dáš

Použijte kód z předchozího cvičení. Upravte vaši šablonu tak, aby z jednotlivých kin generovala řetězce, které obsahují takovéto HTML:

<span class="name">Světozor</span><span class="address">Vodičkova 41, Praha 1</span>
  1. V konzoli vyzkoušejte, že vaše šablona skutečně generuje HTML podle zadaného kina.
  2. Ve vaší aplikaci ze stránky vyberte element s třídou cinema__head a nastavte mu innerHTML na text vygenerovaný vaší šablonou. Vyzkoušejte vygenerovat názvy pro různá kina.
3

Program kina

zapni hlavu

Pokračujte v řešení z předchozího cvičení. Pro jedno kino použije objekt, který vypadá následovně.

const cinema1 = {
  name: 'Světozor',
  address: 'Vodičkova 41',
  movies: [
    {
      title: 'Casablanca',
      time: '18:30',
      hall: 7,
    },
    {
      title: 'Pán prstenů: Dvě věže',
      time: '17:45',
      hall: 4,
    },
  ],
};

Vytvořte Handlebars šablonu, která bude pro každý film vytvářet HTML vypadající takto:

<span class="movie__title">Casablanca</span>
<span class="movie__time">18:30</span>
<span class="movie__hall">sál č. 7</span>

Vezměte první film a pomocí vaší šablony a funkce document.createElement pro něj vytvořte DOM element, který bude vypadat takto.

<div class="movie">
  <span class="movie__title">Casablanca</span>
  <span class="movie__time">18:30</span>
  <span class="movie__hall">sál č. 7</span>
</div>

Ze stránky vyberte element se třídou cinema__movies a pomocí metody appendChild do něj zapojte váš nový DOM element.

Upravte váš předchozí kód tak, že vytvoříte funkci renderMovie, která jako svůj vstup bere jeden objekt filmu. Tato funkce bude vytvářet váš DOM element pro jeden film, který vrátí pomocí return.

Přidejte do objektu s programem kina nějaké další filmy. Pomocí cyklu projděte seznam filmů, pro každý film pomocí renderMovie vytvořte příšlušný DOM element a vložte jej na stránku.

Šablony uvnitř HTML

Je šikovné, že si můžeme šablony připravit takto dopředu. Pořád nám však zbývá jedna nepříjemnost. Naše HTML musíme psát dovnitř řetězců a tím přicházíme o zvýrazňování syntaxe. Navíc máme HTML kód rozházeny po celém JavaScriptovém programu místo toho, abychom jej měli na jednom místě tam, kde bychom jej čekali - uvnitř souboru index.html.

Z této prekérie nám pomůže speciální HTML element s výmluvným názvem template. Tento element můžeme vložit kamkoliv do našich stránek, jeho obsah se však nikde nezobrazí. Element template slouží jako určité místo, kam si můžeme odložit HTML kód, který zobrazíme až pomocí JavaScriptu. Takto si můžeme si můžeme snadno vytvořit šablonu pro naši podcastovou epizodu.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Šablony</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.6/handlebars.min.js"></script>
  </head>
  <body>
    <template id="episode-templ">
      <div class="episode">
        <div class="episode__num">{{num}}</div>
        <div class="episode__body">
          <div class="episode__title">{{title}}</div>
          <div class="episode__guest">{{guest}}</div>
        </div>
      </div>
    </template>

    <div class="episode-list"></div>

    <script src="index.js"></script>
  </body>
</html>

Element s šablonou epizody pak můžeme z dokumentu snadno vybrat a z jeho obsahu zkompilovat Handlebars šablonu.

const episodeTemplate = Handlebars.compile(
  document.querySelector('#episode-templ').innerHTML
);

Pomocí této šablony pak zkonstruujeme DOM element naší epizody. Zde náš čeká jeden technický zádrhel, který nám trochu kazí eleganci kód, ale bohužel nejde nijak obejít.

Všimněte si, že do šablony vkládáme i hlavní div celé epizody s třídou episode. To jsme v předchozích příkladech nedělali. Tento hlavní div jsme si vždy vyrobili pomocí createElement a pak jej jen naplnili obsahem pomocí innerHTML. Tento postup se nám však při použití šablon nehodí. Chceme, aby naše šablona obsahovala HTML celého elementu epizody, ne jen jeho vnitřek.

Pokud však chceme z nějakého řetězce vyrobit DOM elementy, nezbývá nám jiná možnost než použít vlastnost innerHTML nějakého jiného elementu. Zařídíme se tedy tak, že vyrobíme dočasný div, kterému nastavíme innerHTML. Tak uvnitř vyrobíme DOM strukturu naší epizody a pak ji z tohoto dočasného elementu vyjmeme a přesuneme na naši stránku. Náš kód tedy dopadne takto.

'use strict';

const episodeTemplate = Handlebars.compile(
  document.querySelector('#episode-template').innerHTML
);

const episode1 = {
  num: 123,
  title: 'Robot, který snědl koblihu',
  guest: 'Radovan Holátko',
};

const renderEpisode = (episode) => {
  const helperELm = document.createElement('div');
  helperELm.innerHTML = episodeTemplate(episode);
  return helperELm.children[0];
};

const episodesListElm = document.querySelector('.episodes-list');
const elm = renderEpisode(episode1);
episodesListElm.appendChild(elm);

Cvičení - šablony uvnitř HTML

4

Program kina 2

to dáš

Pokračujte v řešení příkladu z cvičení Program kina.

  1. Uvnitř index.html vytvořte šablonu pro element představující jeden film. Nezapomeňte, že šablona musí obsahovat i hlavní div elementu s třídou movie.
  2. Upravte kód pro kompilaci Handlebars šablony tak, aby používal HTML kód vytažený z vašeho template elementu.
  3. Upravte funkci renderMovie tak, aby správně vytvořila element z vaší šablony. Vyhněte se zkládání zbytečného divu pomocí vlastnosti children.
5

Kontakty

zapni hlavu

Vytvořte webovou stránku, která zobrazuje seznam kontaktů. U každého kontaktu bychom chtěli evidovat jméno, příjmení, email a telefonní číslo.

  1. Založte webovou aplikaci se soubory index.html, style.css a index.js.
  2. Do hlavičky souboru index.html vložte odkaz na Handlebars šablony.
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.7.6/handlebars.min.js"></script>
    
  3. Uvnitř index.js si vytvořte pole objektů, které bude obsahovat jednotlivé kontakty.
  4. Uvnitř index.html vytvořte šablonu pro jeden kontakt. Také vytvořte div, do kterého budeme později naše kontakty vkládat.
  5. V souboru index.js si zkompilujte Handlebars šablonu pro jeden kontakt.
  6. Vytvořte funkci renderContact, jejímž vstupem bude objekt kontaktu a výstupem DOM element vyrobený z vaší šablony.
  7. Pomocí cyklu projděte všechny kontakty a zapojte je do stránky.
  8. Nepovinně si můžete pohrát se stylováním. U každého kontaktu se vám například může hodit takováto hezká ikonka.

Struktura projektu

Cvičení - struktura projektu

6

Menu

to dáš

Stáhněte si webovou stránku s jídelním lístkem restaurace.

  1. Prohlédněte si strukturu projektu.
  2. Vyčleňte z projektu komponentu MenuItem. Vytvořte pro ni speciální složku. Všechen JavaScript, který patří ke komponentě vložte do nového souboru MenuItem/index.js a vložte jej do vašeho HTML souboru.
  3. Všechny styly, které patří ke komponentě vyčleňte do souboru MenuItem/style.js a vložte jej do vašeho HTML souboru. Napozemeňte na složku s obrázkem.
  4. Zařiďte, aby aplikace opět správně fungovala.