Dělení šablony na Latte soubory#
Kde která šablona bydlí, jak si systém vybere tu správnou, co do ní přiteče z presenteru a čím se dá skládat.
Tři místa, kde šablony žijí#
| Kde | Co tam patří | Příklad |
|---|---|---|
téma — www/themes/frontend/<šablona>/desktop/layouts/ |
layouty a jejich části: hlavička, patička, obal obsahu | @bazaar_layout_one.latte |
modul — app/UI/Front/<Modul>/Templates/<Presenter>/ |
obsah konkrétní stránky | Advert/list.latte |
komponenta — app/UI/Front/<Modul>/Components/<Komponenta>/Templates/ |
opakovaně použitelný kus výpisu | Default/Style_1.latte |
Dělení podle toho, co se mění s vzhledem
V tématu je to, co se s novou šablonou přepisuje (rozvržení stránky). V modulu to, co je dané funkcí (co stránka vypisuje). Proto se výpis inzerátů dá přebarvit bez sáhnutí do PHP a nová šablona nemusí kopírovat obsah stránek.
Jak si systém vybere šablonu stránky#
Hledá dvě cesty v tomhle pořadí a použije první existující:
1. téma: layouts/modules/<modul>/presenters/<presenter>/<akce>.latte
2. modul: app/UI/Front/<Modul>/Templates/<Presenter>/<akce>.latte
Přesněji: název presenteru se rozseká podle dvojteček, před poslední část se
vloží presenters a všechno se převede na malé první písmeno. Z presenteru
Front:Bazaar:Advert a akce list tak vznikne
layouts/modules/front/bazaar/presenters/advert/list.latte.
Téma může přebít kteroukoli stránku
Stačí položit soubor na cestu 1 a modul se nepoužije. Hodí se, když jedna šablona potřebuje jinak vypsat jeden konkrétní výpis — a nikde se kvůli tomu nevětví PHP.
Když neexistuje ani jedna, uvidíte obě v chybě
Hláška 404 vypíše obě kandidátní cesty v pořadí hledání. Není to seznam „co je špatně“, ale „kam ten soubor můžete dát“.
Uvnitř tématu se navíc zkouší nejdřív varianta zařízení (desktop), pak
desktop jako záloha — takže mobilní varianta může mít vlastní soubor, ale
nemusí.
Jak si systém vybere layout#
Layout NENÍ v kódu — je uložený u stránky v databázi
Každá stránka má v administraci pole layout a druhé pole pro přihlášeného uživatele. Vybere se takhle:
Hledá se v layouts/ tématu. Když tedy stránka vypadá jinak, než čekáte,
nehledejte nejdřív v kódu — podívejte se, jaký layout má nastavený.
@layout.latte je holá kostra, ne výchozí vzhled
Je to nouzový layout Nette. Když se stránka vykreslí bez hlavičky a patičky, skoro jistě má prázdné pole layoutu.
Layouty jsou pojmenované podle modulu a rozvržení — @bazaar_layout_one.latte,
@blog_layout_two.latte, @eshop_layout.latte, @homepage.latte. Varianty
_one/_two/_three se liší počtem a stranou postranních panelů.
Anatomie layoutu#
<!doctype html>
<html lang="{$language->getAlias()}">
{import "parts/shared/head.latte"}
{include head}
<body id="bazaar-page" n:class="$htmlClass ?? '', 'bazaar-page'">
{snippet page}
{var $containerClass='container-lg'}
<div id="toastContainer" class="toast-container toast-container--top-right"></div>
{include 'parts/bazaar/header/default.latte' containerClass => $containerClass}
<main class="site-main">
<section id="content">
{snippet flashMessagesWrapperSnippet}
{include 'parts/shared/main/flash-messages.latte'}
{/snippet}
{include beforeContent}
{include #content} {* ← sem se vloží šablona stránky *}
</section>
</main>
{include 'parts/shared/footer.latte' containerClass => $containerClass}
{/snippet}
{include 'parts/shared/bottom.latte'}
{block scripts}{include 'parts/shared/scripts.latte'}{/block}
{block extraScripts}{/block}
</body>
</html>
| Prvek | Význam |
|---|---|
{include #content} |
místo, kam se vloží blok content ze šablony stránky |
{snippet page} |
obal pro překreslování přes AJAX |
{block scripts} / {block extraScripts} |
stránka si sem může přidat vlastní skripty |
parts/shared/ |
části sdílené všemi moduly (hlava, patička, skripty, hlášky) |
parts/<modul>/ |
části jednoho modulu (hlavička e-shopu se liší od inzerce) |
{import} versus {include}
{import} jen zpřístupní bloky z jiného souboru (nic nevypíše), {include}
je vloží. V hlavičce proto stojí obojí: nejdřív {import "parts/shared/head.latte"},
pak {include head}.
Anatomie stránkové šablony#
{block canonical}
<link rel="canonical" href="{$canonicalUrl}">
{/block}
{define metaTitle}{$categoryTranslation?->getMetaTitle($lang)}{/define}
{define metaDescription}{$categoryTranslation?->getMetaDescription($lang)}{/define}
{block content}
{var $categoryTranslation = $category?->getTranslation($lang)}
<h1>{$categoryTranslation?->getName()}</h1>
{control advertList, $items}
{/block}
| Konstrukce | K čemu |
|---|---|
{block content} |
povinné — obsah stránky, layout ho vkládá |
{define metaTitle} a spol. |
meta údaje; layout si je vyzvedne, když existují |
{block canonical} |
vlastní kanonická adresa |
{control …} |
vykreslení komponenty |
{define} se samo nevypíše
Na rozdíl od {block}. Proto se hodí přesně na meta údaje — layout si je
vyžádá jen tam a tehdy, kdy je potřebuje.
Co do šablony přiteče z presenteru#
Tyhle proměnné máte k dispozici, aniž byste je kamkoli předávali:
| Proměnná | Co je zač |
|---|---|
$page |
entita stránky z databáze |
$lang |
číselné ID jazyka |
$language |
entita jazyka ($language->getAlias() je cs) |
$locale |
locale pro překlady |
$title, $showTitle |
nadpis stránky a příznak, jestli se má vypsat |
$content |
textový obsah stránky z administrace (HTML) |
$metaTitle, $metaDescription, $metaKeywords |
meta údaje ze stránky |
$metaData, $ogData |
doplněná metadata a Open Graph |
$cssList |
seznam CSS souborů navázaných na stránku |
$theme |
entita aktivní šablony |
$defaultUrlParams |
parametry pro sestavování odkazů |
$flashes |
hlášky (viz past níž) |
$lang je ID, ne alias
Do getTranslation() a podobných metod patří $lang. Kdo tam pošle 'cs',
dostane chybu typu — a kdo si alias vezme z presenteru, sáhne vedle úplně.
Alias je $language->getAlias().
Vlastní data si přidá presenter
Cokoli dalšího ($items, $category, $searchHighlights…) přiřazuje
presenter do $this->template. Když proměnná v šabloně chybí, chybí přiřazení
v presenteru — ne v šabloně.
Komponenty#
Komponenta se vykreslí {control jmeno} nebo {control jmeno, argument}.
Šablonu si hledá sama, a to ve třech krocích — první existující vyhrává:
1. Templates/<Šablona>/<Varianta>/Style_1.latte (přesně pro tuhle šablonu a zařízení)
2. Templates/<Šablona>/default/Style_1.latte (pro tuhle šablonu, všechna zařízení)
3. Templates/Default/Style_1.latte (společná výchozí podoba)
Nová šablona nemusí kopírovat všechny komponenty
Přebere Templates/Default/ a přepíše jen to, co chce jinak. Proto většina
komponent v repozitáři má jen výchozí variantu.
Styly a rozpad na části#
Komponenta obvykle nabízí několik podob — Style_1, Style_2, Style_3 (řádek,
dlaždice, kompaktní výpis). Volí se argumentem při vykreslení; renderStyle2() se
automaticky přeloží na Style_2.latte, takže se kvůli tomu nemusí psát metoda.
Opakující se kusy uvnitř stylů patří do podsložky Elements/:
Templates/Default/
├── Style_1.latte ← řádkový výpis
├── Style_2.latte ← dlaždice
├── Style_3.latte ← kompaktní
└── Elements/
├── Actions.latte ← tlačítka, sdílená všemi styly
├── Price.latte
└── UserBadge.latte
Proč zvlášť a ne trojí kopie
Cena se vypisuje ve všech třech stylech stejně. Kdyby byla vepsaná třikrát, změna formátu by se udělala na dvou místech ze tří — a tenhle druh chyby se hledá dlouho, protože stránka vypadá správně, dokud nepřepnete styl.
Styl se dá zvolit jménem#
Kde má komponenta stylů víc, jde vedle čísla použít i jméno — v šabloně se pak
píše {control jmeno:box} místo {control jmeno, 4}. Číslo nikomu neřekne, jak
výpis vypadá; jméno ano.
Sekce stránek (frontSystemPageSection)#
Editovatelný blok šablony (System › Stránky › Sekce) se adresuje klíčem, ne ID ani slugem — klíč zapíše kodér natvrdo do šablony, redaktor pod ním sekci najde v administraci:
{control frontSystemPageSection 'homepage-how'} {* Default: <section class="page-section"> s nadpisem (h2) a obsahem *}
{control frontSystemPageSection:plain 'cta-providers'} {* jen obsah, bez nadpisu a bez obalu — obal si drží šablona sama *}
Obsah je HTML z administrace a vypisuje se 1:1 — žádné skládání, žádné ořezávání. Uložit ho smí jen administrace (RBAC), nikdy vstup od návštěvníka.
Neexistující nebo neaktivní klíč nevykreslí nic
Žádná chyba, žádný zástupný text — komponenta vrátí prázdno a šablona sekci
schová stejně jako jinde :not(:has(...)). Chybějící seed pozná jen vývojář
v ladicím logu (kanál page-section), na produkci je ticho.
Dvě sekce na jedné stránce chtějí multicontrol
Dvě volání se stejným jménem komponenty by si mezi sebou přepisovala
$uniqueId. Druhé (a další) volání proto jde přes multiplikátor s klíčem
sekce jako rozlišovačem:
{multicontrol frontSystemPageSection-homepage-how, 'homepage-how'}
{multicontrol frontSystemPageSection-cta-providers:plain, 'cta-providers'}
Část za pomlčkou (homepage-how) je jen unikátní klíč instance
multiplikátoru — klíč sekce se proto do volání píše ještě jednou, za čárkou,
jako skutečný argument.
Aktuality na hlavní straně (frontBlogIntroList)#
Vypisuje aktivní, už zveřejněná intra (nejvýš pět). Všechny podoby kreslí markup
výpisů .list--* ze základu, takže je šablona stylovala stejnými pravidly jako
články.
| Styl | Volání | Kreslí | Kdy ho použít |
|---|---|---|---|
| Editorial | {control frontBlogIntroList:editorial} |
.list.list--editorial — mřížka karet s miniaturou vlevo, nad titulkem datum, pod ním řádek textu |
Klidný pás pod hlavičkou, kde aktuality nemají přebít zbytek strany. Snese i drobné fotky. |
| Box | {control frontBlogIntroList:box} |
.list.list--box — mřížka karet, obrázek nahoře, pod ním datum, titulek a text |
Nejvíc „e-shopová“ podoba. Chce u každé aktuality fotku; bez ní zůstane zástupný obrázek. |
| Karusel | {control frontBlogIntroList:carousel}, s počtem sloupců {control frontBlogIntroList:carousel, 4} |
.list.list--carousel — vodorovný karusel se šipkami a tečkami (JS carousel.js šablony) |
Když je aktualit hodně a mají se střídat na jednom pruhu. Počet viditelných karet dodá parametr volání (výchozí 3) do --carousel-cols; přepsat ho jde i v CSS sekce. |
| Hero | {control frontBlogIntroList:hero} |
.list.list--carousel.list--carousel-hero — jedna karta přes celou šířku, fotka přes celou plochu a text v přechodu přes ni, přepíná se vodorovně |
Vodorovné dvojče svislého karuselu. Šipky stojí svisle na střed u obou okrajů, tečky dole na střed. Chce velkou fotku na šířku. |
| Svislý karusel | {control frontBlogIntroList} |
.list.list--carousel-vertical — jedna karta přes celou šířku, přepíná se svisle |
Výchozí podoba. Jedna velká fotka s popiskem místo výpisu. |
| Bootstrap karusel | {control frontBlogIntroList:style2} |
Bootstrap .carousel s popiskem přes fotku |
Starší podoba, ponechaná kvůli šablonám, které ji už používají. |
Sekce se schová, když v ní není .list-item
Šablony si sekci s aktualitami běžně schovávají pravidlem
&:not(:has(.list-item)) { display: none; }, aby prázdný výpis nenechal na
straně díru. Styl Editorial ale kreslí .list-item--small (tak to má
_editorial.scss), ne .list-item — v takové sekci by zmizel i s obsahem.
Podmínku je pak potřeba rozšířit o .list-item--small.
Pasti#
|noescape nikdy do atributu
Hodnota připravená jako bezpečné HTML platí pro tělo elementu. V title=
nebo alt= je stejné escapování k ničemu. Do atributů patří holý text.
{snippet} má vlastní rozsah proměnných
Proměnná nastavená {var} mimo snippet uvnitř něj po překreslení nemusí
existovat — při AJAXu se vykreslí jen ten kus. Co snippet potřebuje, ať si
nastaví uvnitř, nebo ať to přijde z presenteru.
Jméno snippetu musí být literál
{snippet "row-$id"} se nepřeloží. Dynamické části řeší {snippetArea}
a n:snippet na prvku cyklu.
AJAX přepíše jen vnitřek snippetu
Atributy elementu, na kterém snippet visí (třídy, data-), zůstanou původní.
Když se má měnit i obal, musí být uvnitř.
n:if na poli formuláře zahodí jeho hodnotu
Pole se nevykreslí, takže se ani neodešle — a při uložení se zapíše prázdno. Skrývat se má obal, ne samotné pole.
Cesty v {include} jsou relativní k souboru, ne ke kořenu
Přesun šablony do jiné složky rozbije všechny její vnořené includy.
Hlášky se na frontu mění na plovoucí oznámení
Vypsaná hláška se skriptem přesune do #toastContainer a z původního místa
zmizí. Kdo ji hledá v DOM tam, kde ji vypsal, nenajde ji.
V šabloně komponenty není překladač s doménou
Překládá se v presenteru nebo v komponentě v PHP a do šablony jde hotový text.
Kde co hledat#
| Chci změnit | Soubor |
|---|---|
| hlavičku, patičku, rozvržení stránky | www/themes/<šablona>/desktop/layouts/ |
| co konkrétní stránka vypisuje | app/UI/Front/<Modul>/Templates/<Presenter>/<akce>.latte |
| podobu opakovaného prvku výpisu | …/Components/<Komponenta>/Templates/Default/Style_N.latte |
| barvy, rozestupy, typografii | assets/scss/custom/ — styly šablony |
| jak má prvek vypadat správně | katalog komponent |