define → embed zamyšlení
- medhi
- Generous Backer | 255
Ve verzi 2.10 přibyla možnost embedovat část šablony, která je
definována makrem {define}
, viz příklad z dokumentace:
{define collapsible}
<section class="collapsible {$modifierClass}">
<h4 class="collapsible__title">
{block title}{/block}
</h4>
...
</section>
{/define}
{embed collapsible, modifierClass: my-style}
{block title}
Hello World
{/block}
...
{/embed}
To je naprosto super, protože já to používám například na vložení modal okna v různých místech aplikace.
U původního použití ({embed './tam/nekde/je/modal.latte'}
)
mě trochu obtěžovalo vymýšlet aktuální cestu k původnímu souboru,
který embeduji. Zde to odpadá, což je super, ale bohužel to má zase jinou
nevýhodu – blocky v {define collapsible}
nemají vlastní scope
a pokud je {title}
už definován někde v šabloně, nejde znovu
použít.
Je to logické, jen to tak zmiňuji k zamyšlení, protože by se mi líbila
myšlenka nadefinovat si X bloků pomocí {define}
v extra
souborech, potom je importovat pomocí {import}
do
@layout.latte
a pak je moci používat bez dalšího přemýšlení
pomocí {embed modal}
.
Zajímá mě váš pohled, možná pro stromy nevidím les a šlo by to celé řešit mnohem elegantněji.
Díky
- David Grudl
- Nette Core | 8218
Chápu co chceš a přemýšlím, jak to udělat. Vytvářet novou značku
bych nerad, protože už dnes řada lidí nerozumí rozdílu mezi block/define.
Tak možná nějaký modifikátor? {define xxxx scope}
? Teda
samozřejmě s nějakým lepším slovem než scope.
- medhi
- Generous Backer | 255
Nelíbí se mi přidávat další neznámou funkcionalitu, kterou si musím zapamatovat. Líbí se mi, že se Latte snaží být analogií k PHP, takže by mi dávalo smysl toto:
{define collapsible}
<section class="collapsible {$modifierClass}">
<h4 class="collapsible__title">
{block private title}{/block} {* tento blok je viditelný pouze v aktuální bloku, tedy v collapsible *}
</h4>
...
</section>
{/define}
{embed collapsible, modifierClass: my-style}
{block title}
Hello World
{/block}
...
{/embed}
Z toho by vyplývalo, že jako výchozí scope bloků je public, tedy
{block foo}
je alias k {block public foo}
. Použil
bych to samozřejmě i pro {define private foo}
, apod.
Je to jenom nápad, předpokládám, že implementace nemusí dávat smysl, nebo nemusí být možná nebo to může být zpětně nekompatibilní.
Myšlenka je, zavést public/private pro všechny bloky v Latte. Tento typ
scopu by nesouvisel se souborovým rozdělením (stejně jako v PHP), to
obstarává slovíčko local
(jestli jsem to dobře pochopil).
Řešilo by to pouze scopy bloků v úrovni celého stromu šablon nezávisle na souborech.
- David Grudl
- Nette Core | 8218
To mi moc smysl nedává. Pokud zůstanu u analogie OOP, tak chceš, aby potomek (embed) přepisoval private metody rodiče (define), ne?
- medhi
- Generous Backer | 255
Otázka je, jestli define -> embed
chápat jako dedičnost
bloků. Já to tak nevnímám. Vnímám to tak, že {embed}
je
pouze vykreslení bloku s tím, že mu nacpu nějaké hodnoty. Stejné jako
zavolání funkce třeba.
V tomto kontextu jsem ten parametr private
chápal spíš tak,
že je odříznutý od okolí, tedy od bloku nad {define}
.
Jde mi hlavně o určení si, jaké bloky mají být privátní a jaké ne.
Tvůj nápad s {define xxxx scope}
by to uzavřel celé a nešlo by
využít žádné bloky zvenčí.
- David Grudl
- Nette Core | 8218
Pro vykreslení stránky s tím, že přepíšu nějaké bloky, je termín dědičnost zaužívaný. A nejen teda v Latte, ale vlastně ve všech obdobných šablonovacích systémech co znám. Takže i další terminologie by tomu měla odpovídat a tudíž private blok by měl být blok, který zvenčí ovlivnit nemůžu, ne naopak.
{define xxxx scope} by to uzavřel celé a nešlo by využít žádné bloky zvenčí.
V první postu píšeš „blocky v {define collapsible} nemají vlastní scope“, takže jsem chápal, že ti jde o scope.
Hele je možné, že se bavíme o úplně různých věcech. Můžu poprosit o příklad, kde by bylo všechno vidět? Co to má dělat a čemu to naopak nemá zabránit?
- medhi
- Generous Backer | 255
V podstatě to celé potřebuji pouze na znovu použitelné HTML části kódu, kterým je občas potřeba předat ne jen pouze nějakou proměnnou, ale i kus HTML. Typicky je to modal okno Bootstrapu, kdy si někde definuji jeho kostru a potom na různých místech chci tuto kostru vložit a do ní vložit třeba nadpis a HTML tělo.
Nebo nějaký box, do kterého zase vkládám HTML atd.
{embed}
je jediný způsob, jak vkládat blok a do něj
definovat kus HTML. Ale jak jsem psal výše, nebaví mě řešit ty cesty, tak
jsem kvitoval tu novinku z 2.10. Moje představa potom byla taková:
Soubor _modal.latte:
{define modal}
<div class="modal fade" id="{$modalId}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" >{block title}{/block}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
{block body}{/block}
</div>
</div>
</div>
</div>
{/define}
Soubor @layout.latte, kde si všechny tyto prvky naimportuju:
{import _modal.latte}
{import _infobox.latte}
A pak někde v šabloně:
{embed modal, modalId: 'signIn'}
{block title}Přihlásit se{/title}
{block body}
... formulář a html
{/block}
{/embed}
Což selže, protože {block title}
už je definovaný někde
jinde a já nemám názvy bloků v {define}
ve scopu.
- David Grudl
- Nette Core | 8218
Jasně, tahle jsem to chápal. Zmátlo mě „Tvůj nápad s {define xxxx scope} by to uzavřel celé a nešlo by využít žádné bloky zvenčí.“
Co myslíš těmi bloky zvenčí? Jsou v tom příkladu?
- medhi
- Generous Backer | 255
Máš pravdu, to jsem do toho ještě vnesl „myšlenku navíc“, kdy by šly používat i bloky zvenčí, tedy mohl bych udělat třeba toto:
{define modal}
<div class="modal fade" id="{$modalId}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" >{include title}: {block private title}{/block}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
{block body}{/block}
</div>
</div>
</div>
</div>
{/define}
Protože nechceš přijít o původní funkcionalitu, tedy možnost použít existující bloky zvenčí.
Ale nyní už vidím, že asi bude nejlepší použít unikátní název bloku a vše se tím vyřeší.
- David Grudl
- Nette Core | 8218
Jj, tady by unikátní název rozhodně prospěl čitelnosti. Lepší by bylo
<h5 class="modal-title" >{include mainTitle}: {block title}{/block}</h5>
Chování include je teď takové, že nejprve blok hledá v local a poté
v aktuálním scope (což nemusí být globální, např. uvnitř embed). Tedy
{include mainTitle}
uvnitř (zatím hypotetického) {define
scoped} by lokální blok mainTitle načetlo. Samozřejmě by to šlo
upravit, aby nakonec zkusil i scope globální. Ale asi bych počkal, jestli
něco takového bude potřeba.
- David Grudl
- Nette Core | 8218
Ještě přemýšlím nad jednou věcí, a to jestli mají být v takových
{define}
dostupné jako lokální proměnné globální proměnné
šablony (tj. proměnné převádané do $template v presenteru). Spíš bych
řekl, že ne. Co myslíš?
- medhi
- Generous Backer | 255
David Grudl napsal(a):
Ještě přemýšlím nad jednou věcí, a to jestli mají být v takových
{define}
dostupné jako lokální proměnné globální proměnné šablony (tj. proměnné převádané do $template v presenteru). Spíš bych řekl, že ne. Co myslíš?
Myslíš v tom hypotetickém {define scoped}
? Hm, asi ne. Ale
i tak bude zvláštní, že tam bude viditelný ten
{include mainTitle}
, pokud bude lokální. Bude to vlastně jediná
věc, co tam bude vidět. Otázka je proč? A pokud už tam teda vidět bude a
nebude tam vidět globální, tak to bude zase nějaké jiné další
chování.
BTW 1: Není divné, že v {define}
můžeš includovat
lokální bloky, ale nemůžeš tam vypsat lokální proměnnou? Neříkám, že
je to špatně, jenom jsem si to uvědomil a přijde mi to zajímavé.
BTW 2: Pokud použiju {embed 'soubor.latte'}
a v
soubor.latte
si vše nadefinuji jako local, tak jsem vlastně
v pohodě a můžu klidně použít title
, aniž bych řešil,
jestli je někde v okolí, je to tak?
- David Grudl
- Nette Core | 8218
Přečetl jsem si tvůj post asi 5× a nechápu ani větu :-) Zkusím to zítra znovu :-)
- David Grudl
- Nette Core | 8218
Ale i tak bude zvláštní, že tam bude viditelný ten {include mainTitle}, pokud bude lokální. Bude to vlastně jediná věc, co tam bude vidět. Otázka je proč? A pokud už tam teda vidět bude a nebude tam vidět globální, tak to bude zase nějaké jiné další chování.
Vadí ti, že jsou lokální bloky inkludovatelné z embed (a byly by dostupné z define scoped)? Pokud ano, proč? Nebo vadí, že nejsou inkludovatelné globální bloky? Pokud ano, proč? Nebo jak by to mělo být?
Není divné, že v {define} můžeš includovat lokální bloky, ale nemůžeš tam vypsat lokální proměnnou? Neříkám, že je to špatně, jenom jsem si to uvědomil a přijde mi to zajímavé.
Proč bys nemohl vypsat lokální proměnnou? Vypsání lokální proměnné
je {$var}
. Jak to myslíš?
Pokud použiju {embed ‚soubor.latte‘} a v soubor.latte si vše nadefinuji jako local, tak jsem vlastně v pohodě a můžu klidně použít title, aniž bych řešil, jestli je někde v okolí, je to tak?
Když si vše nadefinuješ local, tak embed ztrácí smysl (tj. chová se stejně jako include), protože žádný blok nemůžeš přepsat.
Potřebuju, abysme si uplně bezchybně rozuměli, proto se možná ptám jako robot :)
- medhi
- Generous Backer | 255
Vadí ti, že jsou lokální bloky inkludovatelné z embed (a byly by dostupné z define scoped)? Pokud ano, proč? Nebo vadí, že nejsou inkludovatelné globální bloky? Pokud ano, proč? Nebo jak by to mělo být?
Pravda je, že include znamená, že účelově odněkud něco chci, takže automaticky počítám s tím, že to už venku existuje. Takže by asi mělo jít includovat i bloky zvenčí. Ale když už, tak i globální. Jaký je důvod, že globální nejdou a lokální ano?
Proč bys nemohl vypsat lokální proměnnou? Vypsání lokální proměnné je {$var}. Jak to myslíš?
Vycházím z dokumentace: Definice nemají přístup k proměnným
aktivního kontextu, ale mají přístup k globálním proměnným.
Pochopil jsem možná špatně, že „proměnné aktivního kontextu“ jsou
jakože lokální. Možná jsem jenom nepochopil názvosloví.
Každopádně tedy abych to upřesnil: V define lze includovat lokální bloky,
ale nelze tam vidět proměnné aktivního kontextu. Netvrdím, že je to
špatně, jenom mi to přijde jako nesoulad.
Když si vše nadefinuješ local, tak embed ztrácí smysl (tj. chová se stejně jako include), protože žádný blok nemůžeš přepsat.
Aha, ok.
- Milo
- Nette Core | 1283
Snad do toho nevnesu ještě více zmatku. Je tohle workaround?
{define collapsible}
<section class="collapsible {$modifierClass}">
<h4 class="collapsible__title">
{block title}{/block}
</h4>
</section>
{/define}
{embed collapsible, modifierClass: my-style}
{block title}{include parent}{/block} {* Workaround *}
{/embed}
To {define xxx scope}
mi nesedí. To spíš
{block title overloadable-by:file}{/block}
, což je samozřejmě
haluz, ale nějak, aby si blok sám mohl říct, odkud může být v hierarchii
přetížen.
- Milo
- Nette Core | 1283
Tak se mi to povedlo :-)
Aby mu blok title
nepřepsal blok definovaný dříve.
{define collapsible}
<section class="collapsible {$modifierClass}">
<h4 class="collapsible__title">
{block title}Výchozí obsah{/block}
</h4>
</section>
{/define}
{embed collapsible, modifierClass: my-style}
{block title}{include parent}{/block} {* Aby tady nemusel zase psát "Výchozí obsah" a aby mu to náhodou nepřepsal jiný blok *}
{/embed}
- David Grudl
- Nette Core | 8218
Jo, to lze. Ale neříkal bych tomu workaround, protože přesně takhle je to navrženo, aby se to používalo. https://github.com/…d.block.phpt#…
- David Grudl
- Nette Core | 8218
@medhi
Takže by asi mělo jít includovat i bloky zvenčí. Ale když už, tak i globální. Jaký je důvod, že globální nejdou a lokální ano?
Kvůli předvídatelnosti chování.
- Lokální bloky jsou vždy zřejmé. Jsou to bloky definované v souboru, kde se nachází i samotné include.
- Globální bloky jsou naopak mlhavé. Máme soubor s layoutem, importované soubory, soubory importované z importovaných souborů – kdekoliv vznikne blok, tak je to globalní blok.
- a uvnitř {embed} jsou bloky definované v embedovaném souboru
Takže mám tu soubor s lokálním blokem a také značkou embed a dále embedovaný soubor s blokem.
Když uvnitř embed použiji include, načte buď lokální blok (s vyšší prioritou), nebo v embedovanem souboru definovaný blok.
Proč. Pokud dojde ke změně v embedovaném souboru a nějaký blok se přejmenuje, je žádoucí, aby includování bloku pod původním názvem vyhodilo vyjímku. Určitě není žádoucí, aby místo toho tiše vkládalo bůhvíjaký globální blok, protože mají stejný název. Nebo případně tiše vkládalo lokální blok téhož názvu.
Dále pokud dojde k jmennému konfliktu a lokální blok a blok v embedovaném souboru se jmenují stejně, konflikt je lepší řešit přejmenováním lokálního bloku, na který není ze žádného jiného souboru vazba, než v embedovaném souboru, ke vazby jsou. Stejně tak když v embedovaném souboru vznikne nový blok který se jmenuje stejně jako lokální, na chování include to nemá vliv. Proto má lokální blok prioritu před blokem z embedovaného souboru.
- David Grudl
- Nette Core | 8218
Vycházím z dokumentace: Definice nemají přístup k proměnným aktivního kontextu, ale mají přístup k globálním proměnným. Pochopil jsem možná špatně, že “proměnné aktivního kontextu” jsou jakože lokální. Možná jsem jenom nepochopil názvosloví.
Přesněji řečeno, cokoliv co vložím přes {include ...}
nemá přístup k proměnným v kontextu (jo, jsou to
lokální proměnné, ale v místě značky {include}
). Je tu teda
jedna výjimka, ale o to teď nejde. Naopak globální proměnné (ty předané
do $template) jsou k dispozici všude. Což právě raději u těch {define}
spíš
omezil.
Každopádně tedy abych to upřesnil: V define lze includovat lokální bloky, ale nelze tam vidět proměnné aktivního kontextu. Netvrdím, že je to špatně, jenom mi to přijde jako nesoulad.
- Lokální blok je blok definovaný ve stejném souboru jako {define}. Blok o jehož podobě a existenci zcela jasně vím. Mohu ho includovat.
- Lokální proměnné v místě, kde se volá {include}, jsou úplně random proměnné o kterých nevím nic. Uvnitř define je používat nechci, a to ani třeba omylem, takže vůbec nechci, aby tam byly k dispozici.