define → embed zamyšlení

medhi
Generous Backer | 255
+
0
-

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

Felix
Nette Core | 1189
+
+1
-

Resil jsem neco podobneho s tou cestou. Pridal jsem si globalni promennou $embedPath a najednou je to pouziti o trochu jednodussi.

{embed $_embedPath/modal.latte}

Slo by to pak kombinovat ruzne, napr. dat si celou cestu do promenne.

{embed $embbeded->modal}
David Grudl
Nette Core | 8129
+
0
-

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
+
0
-

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 | 8129
+
0
-

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
+
0
-

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 | 8129
+
0
-

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
+
0
-

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">&times;</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 | 8129
+
0
-

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
+
0
-

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">&times;</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 | 8129
+
0
-

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 | 8129
+
0
-

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
+
0
-

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 | 8129
+
0
-

Přečetl jsem si tvůj post asi 5× a nechápu ani větu :-) Zkusím to zítra znovu :-)

medhi
Generous Backer | 255
+
0
-

David Grudl napsal(a):

Přečetl jsem si tvůj post asi 5× a nechápu ani větu :-) Zkusím to zítra znovu :-)

Chápu, sám jsem to asi 5× přepisoval a je už dost pozdě. Kdybys chtěl, jsem k dispozici i přes nějaké audio. Díky moc :)

David Grudl
Nette Core | 8129
+
0
-

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
+
0
-

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
+
0
-

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.

David Grudl
Nette Core | 8129
+
0
-

Už jsem zmatený dokonale :-) Pro co by to měl být workaround?

Milo
Nette Core | 1283
+
0
-

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 | 8129
+
+1
-

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 | 8129
+
0
-

@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 | 8129
+
0
-

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.
medhi
Generous Backer | 255
+
0
-

Díky za odpovědi. Věřím, že je to vymyšleno správně, jenom jsem se v tom úplně neorientoval.