Šablony a jak vylepšit makro {attr …}
- David Grudl
- Nette Core | 8218
Makro {attr ...}
sice představuje docela silný prostředek pro
zápis dynamických HTML atributů, ale jeho syntaxe se mi moc nelíbí. Docela
rád bych vytvořil šikovnějšího nástupce. Chtěl jsem se proto zeptat,
jakými způsoby toto makro používate?
- Petr Motejlek
- Člen | 293
Jelikož je v dokumentaci už dlouho napsáno, že je to ve vývoji, tak za sebe píšu, že já ho nepoužívám vůbec ;).
- Blizzy
- Člen | 149
Používám ho jen pokud chci vepsat celé pole hodnot do parametru,
většinou class
:
<li{attr class($classes)}>
Do pole $classes
předtím nacpu třídy jako last
,
first
, active
aj. A protože se může stát, že
pole zůstane prázdné, takto se to dá elegantně vypsat.
V ostatních případech si vystačím s {if}
makrem…
(i když to ve výsledku nevypadá tak hezky a čistě jako s
{attr}
)
Editoval Blizzy (19. 8. 2009 11:52)
- David Grudl
- Nette Core | 8218
Hmmm, já to taky v životě na nic jiného než class
nepoužil (no jednou na style, ale to se nesmí říkat bo style přece frajeři
nepoužívají).
No a právě tohle se mi nelíbí:
<li{attr class('first', $iterator->isFirst()) class('last', $iterator->isLast())}>
Schválně nebudu prozrazovat svůj tip na lepší syntax, ať neovlivňuju a zeptám se – napadá vás nějaká lepší syntax?
- Vitek Jezek
- hledá kolegy | 285
posledni posledni sobota pravi:
kazdopadne si myslime, ze z {attr …} by melo byt {class } (na nic jineho to zkratka nema smysl). Alternativni zapis v podobe n:… je samozrejmosti.
Zbyva vyresit syntax – mame hned dve reseni:
psat podminku za nazev tridy, oddelit zavorkami
{class item odd($iterator->isOdd()) last($iterator->isLast())}
+ prehlednost (?) - pro kodera asi ano
- horsi implementace
psat podminku pred tridu, oddelit otaznickem
(neni ta syntax nejak povedoma? : )
{class item $iterator->isOdd()?odd $iterator->isLast()?last}
o prehlednost diskutabilni (pro programatora mozna, pro kodera asi ne)
+ lepe implementovatelne
+ umoznuje i else -> $iterator->odd?odd:even
ocekavany vystup obou reseni na lichem poslednim radku:
class="item odd last"
Editoval Whitek (30. 8. 2009 1:12)
- David Grudl
- Nette Core | 8218
Teda nelíbí se mi ani jedno ;)
Syntax by měla být co nejvíce HTML-like. Dokonce si myslím, že by nebylo
od věci, aby šablonovací systém na výskyt maker uvnitř prostoru HTML tagu
(a mimo hodnoty atributů) dokázal upozorňoval jako na něco špatného.
Např. <div {foreach...}...{/foreach}>
.
Ale k těm atributům. Obecně se dá říct, že existují dva případy:
- atribut s hodnotou, který se vykreslí pouze pokud je splněna určitá
podmínka
- speciální případ, kdy samotná hodnota je zároveň i podmínkou
(vykresli
title="{$title}"
když existuje$title
)
- speciální případ, kdy samotná hodnota je zároveň i podmínkou
(vykresli
- atribut tvořený seznamem volitelných hodnot, který se vykreslí pouze
pokud existuje alespoň jedna hodnota
- v HTML takových atributů existuje celá řada, ale v praxi se z nich
využije v podstatě jen
class
- v HTML takových atributů existuje celá řada, ale v praxi se z nich
využije v podstatě jen
Návrh syntaxe:
ad 1)
<span {$cond?}title="Nejaky title" lang="cs">...</span>
<span title="Nejaky title"{?$cond} lang="cs">...</span> // tohle je kolidující s {? ... }
<span {$cond:}title="Nejaky title" lang="cs">...</span>
<span title="Nejaky title"{:$cond} lang="cs">...</span>
atd...
ad 2)
Pokud by vzniklo n:class
, bylo by potřeba použít PHP-like
zápis, tedy PHP s doplňováním uvozovek kolem identifikátorů:
<li n:class="item, odd => $iterator->odd, last => $iterator->last">
Druhou možností je rozšířit bod č.1, aby fungoval i uvnitř hodnot atributů:
<li class="item {$iterator->odd?}odd {$iterator->last?}last">
<li class="item odd{?$iterator->odd} last{?$iterator->last}"> // tohle je kolidující s {? ... }
<li class="item {$iterator->odd:}odd {$iterator->last:}last">
<li class="item odd{:$iterator->odd} last{:$iterator->last}">
Zde by jen bylo trošku implementačně náročnější zajistit, aby
nevznikal prázdný class=""
.
Ale když už by se šlo touto cestou, taky by bylo možná vhodnější řešení č. 1 upravit tak, aby se podmínky používali také jen uvnitř atributů, na speciálním místě:
<span title="{?$cond}Nejaky title" lang="cs">...</span> // tohle je kolidující s {? ... }
<span title="Nejaky title{$cond:}" lang="cs">...</span>
<span title="{:$cond}Nejaky title" lang="cs">...</span>
- kravčo
- Člen | 721
Mne je najviac sympatická otázniková notácia:
<span{if $selected} class="selected"{/if}>...</span>
<!-- vs. -->
<span {$selected?}class="selected">...</span>
Prispieva k čitateľnosti a prefixová forma mi príde prirodzenejšia ako
suffixová, píšeme predsa if <cond> then <smth>;
. Je
podobná makru {?...}
, ale myslím, že to pomaly zo šablón
vymizne…
Zápis n:class
je podľa mňa ideálny, všetky n:*
atribúty používajú vnútri php-like syntax, takže tu nemám
čo dodať.
David Grudl napsal(a):
Ale když už by se šlo touto cestou, taky by bylo možná vhodnější řešení č. 1 upravit tak, aby se podmínky používali také jen uvnitř atributů, na speciálním místě:
Ak bude podporovaná syntax vnútri atribútov, myslím, že špeciálne miesto nie je nutné, nasledovné zápisy by mali byť ekvivalentné:
<span {$selected?}class="selected">...</span>
<!-- vs. -->
<span class="{$selected?}selected">...</span>
Je fakt, že v prípade viacerých tried by to bolo trochu komplikované…
<span {$selected?}class="selected hilite">...</span>
<!-- vs. -->
<span class="{$selected?}selected {$selected?}hilite">...</span>
Ale tento príklad je dosť umelý a nevidím jeho praktické využitie…
- Honza Marek
- Člen | 1664
Tohle se mi jediné líbí, všechny ostatní způsoby mi přijdou mimořádně neintuitivní.
<li n:class="item, odd => $iterator->odd, last => $iterator->last">
Myslím, že by to šlo rozšířit i na další atributy syntaxí jako
n:attr-class
, n:attr-style
atd.
kravčo napsal(a):
Je podobná makru{?...}
, ale myslím, že to pomaly zo šablón vymizne…
Ještě pořád potřebuju tohle makro na {? Debug::dump(...)}
,
protože makro dump
používá console dump a ten mi nějak není
sympatický.
- jakubkulhan
- Člen | 55
Jedna syntaxe pro inline podmínky je všem známá – ternární operátor – proto bych se co nejvíce držel jej. A určitě bych dal takovým makro-atributům nějaký prefix.
<!-- Že se jedná o atribut se dá najevo závorkami: -->
<span n:{class}="$selected ? selected : not-selected"></span>
<!-- V případě že chceme více tříd, tak se použije grouping pomocí závorek: -->
<span n:{class}="$selected ? (selected hilite) : not-selected"></span>
<!-- Větěv za `:` nemusí být přítomna: -->
<span n:{class}="$selected ? selected"></span>
<!-- A pokud je sama proměnná podmínkou: -->
<span n:{title}="$title?"></span>
<!-- možná pro zvýraznění, že chceme vypsat proměnnou před otazníkem: -->
<span n:{title}="$title?<"></span>
<!-- Více podmínek: -->
<span n:{class}="($selected ? selected) ($iterator->last ? last)"></span>
- Cifro
- Člen | 245
<li n:class="item, odd => $iterator->odd, last => $iterator->last">
Trochu zamachrujem, lebo som si pozrel niekoľko videii o RoR :-))
<li n:class="cycle(first, odd, even, last)">
by vyhodilo
<li class="firt even">
<li class="odd">
<li class="even">
<li class="last odd">
To by mohlo byť zjednodušenie pre iteráciu.
A pre iné prípady je toto celkom dobre čo napísal jakubkulhan
- PetrP
- Člen | 587
Nezaváděl bych další magickou wtf syntaxi.
Všechny macra fungujou principem {nazevMacra...}
nebo
n:nazevMacra="..."
Nezaváďel bych další nejakou šílenost která moc nedává smysl: mluvim
o tech řešeních typu class="{$cond?}neco neco2 {$cond?}neco3"
kdy to ve vysledku ovlivní i vypsání/nevypsání class=""
K jednotlivím davidovým návrhúm:
<span {$cond?}title="Nejaky title" lang="cs">...</span> <span {$cond:}title="Nejaky title" lang="cs">...</span> <span title="Nejaky title"{:$cond} lang="cs">...</span>
Neliší se od {if $cond}title="Nejaky title"{/if}
if je jen
o par znaku delsi, a nema to wtf faktor, a ani nekompabilitu. A vlastně to
ani nic neřeší.
<span title="Nejaky title"{?$cond} lang="cs">...</span> // tohle je kolidující s {? ... }
Není spětně kompatibilní takže o ní ani nemuzu uvažovat (ale platí pro ní to samé co pro předešlé)
<li n:class="item, odd => $iterator->odd, last => $iterator->last">
Tohle je aspoň trošku rozumné, a mluvili jsme o tom i na poslední sobotě.
<li class="item {$iterator->odd?}odd {$iterator->last?}last"> <li class="item odd{?$iterator->odd} last{?$iterator->last}"> // tohle je kolidující s {? ... } <li class="item {$iterator->odd:}odd {$iterator->last:}last"> <li class="item odd{:$iterator->odd} last{:$iterator->last}">
Tohle je prostě wtf, není to ani klasicke macro, zavádí to novou syntaxy, a ovlivnuje to něco mimo macro.
- David Grudl
- Nette Core | 8218
Co je špatného na zavádění nové syntaxe?
Zkuste si projít všechny šablonovací systémy na wiki – to je bída, tam těžko hledat nějakou inspiraci. Ale jakmile jste leaderem, tak prostě vymýšlíte nová řešení. Nette je framework odpočátku založený na hledání nových cest.
Cílem je: velmi pohodlný šablonovací systém se syntaxí srozumitelnou pro neprogramátora. Od tohoto konce se musí začít a pak jen hledat cesty, jak to implementovat. Takže otázka zní:
- jak by se nám psaly šablony hezky?
- co v nich musíme dnes řešit krkolomně?
- David Grudl
- Nette Core | 8218
buff napsal(a):
Mně se na tom trochu nelíbí, že ty to
class="
"
napíšeš mimo jakákoliv makra, ale stejně chceš, aby se v případě prázdného vnitřku vymazalo. Podle mě je to dost WTF-like chování. Nic konstruktivního však zatím nemám, pardon. ;-) Třeba později…
Jj, ačkoliv je to otázka toho, jak moc šablonovací systém „chápe“ HTML a uživatel je s tím srozuměn.
kravčo napsal(a):
Ak bude podporovaná syntax vnútri atribútov, myslím, že špeciálne miesto nie je nutné, nasledovné zápisy by mali byť ekvivalentné:
<span {$selected?}class="selected">...</span> <!-- vs. --> <span class="{$selected?}selected">...</span>
Tohle by právě mohlo vést k
<span>...</span>
<!-- vs. -->
<span class="">...</span>
jakubkulhan napsal(a):
Jedna syntaxe pro inline podmínky je všem známá – ternární operátor – proto bych se co nejvíce držel jej. A určitě bych dal takovým makro-atributům nějaký prefix.
Taky je trošku problém v tom, že obsah n:atributů je v podstatě PHP,
eventuálně vylepšené o doplňování apostrofů kolem řetězců –
neidentifikátorů. Takže $selected ? selected : notselected
je
ok, kdežto $selected ? selected
nebo $title?
už
poměrně těžko řešitelné (znamenalo by to extra parser, tedy nový jazyk).
Přičemž v praxi bude v 99 % případů potřeba právě tohle.
Cifro napsal(a):
Trochu zamachrujem, lebo som si pozrel niekoľko videii o RoR :-))
<li n:class="cycle(first, odd, even, last)">
To si myslím je už dnes realizovatelné. (vytvořením fce cycle).
PetrP napsal(a):
Nezaváďel bych další nejakou šílenost která moc nedává smysl: mluvim o tech řešeních typu
class="{$cond?}neco neco2 {$cond?}neco3"
kdy to ve vysledku ovlivní i vypsání/nevypsáníclass=""
Teď nevím, jestli ti vadí nevypsání class (což chápu, to je magie) nebo zjednodušení užití if (což moc nechápu, syntactic sugar rulez).
Tohle je prostě wtf, není to ani klasicke macro, zavádí to novou syntaxy, a ovlivnuje to něco mimo macro.
To je jako zkrácený ternární operátor. Proč by nemohlo být pro
{if $cond}xxx{/if}
zkrácené {$cond?}xxx
– pokud
by to učinilo šablony čitelnější? Srovnej:
<li class="item {$iterator->odd?}odd {$iterator->last?}last">
vs.
<li class="item {if $iterator->odd}odd{/if} {if $iterator->last}last{/if}">
Je totéž jako
if ($iterator->odd) odd();
vs.
if ($iterator->odd): odd(); endif;
- Honza Marek
- Člen | 1664
Tohle vám nepřijde dost dobré?
<li class="{= $active ? 'active' : ''}">obsah</li>
David Grudl napsal(a):
<span>...</span> <!-- vs. --> <span class="">...</span>
Jakej je mezitim v praxi rozdíl?
- David Grudl
- Nette Core | 8218
Honza M. napsal(a):
Jakej je mezitim v praxi rozdíl?
V případě atributu class
pouze vizuální, ale například
u povinného atributu alt
je
<img src="..." alt=""> OK
<img src="..."> CHYBA
nebo naopak u atributu s předepsaným formátem je:
<span id=""> CHYBA
nebo
<span lang=""> CHYBA
- jakubkulhan
- Člen | 55
David Grudl napsal(a):
Taky je trošku problém v tom, že obsah n:atributů je v podstatě PHP, eventuálně vylepšené o doplňování apostrofů kolem řetězců – neidentifikátorů. Takže
$selected ? selected : notselected
je ok, kdežto$selected ? selected
nebo$title?
už poměrně těžko řešitelné (znamenalo by to extra parser, tedy nový jazyk). Přičemž v praxi bude v 99 % případů potřeba právě tohle.
Nové jazyky jsou super – ať žijí DSL :-) Zas tak těžko řešitelné to není, parser pro to by nebyl tak složitý. Na http://bukaj.netuje.cz/…_attrs.patch je patch, který přidává podporu pro „chytré“ atributy.
Jsou tam menší změny oproti tomu, co jsem psal výše. Jsou dva typy
chytrých atributů – povinné a nepovinné. Povinné se zapisují
n:(attr)="..."
a pokud žádné z podmínek nevyhoví, zůstane
tam prázdný atribut (např. alt
u <img>
apod.). Pak jsou tu nepovinné – n:[attr]="..."
. U těch, pokud
žádné podmínka nevyhoví, tak se atribut prostě nepřidá.
V těle atributu ("..."
) je buď jedna podmínka, nebo více.
Pokud je jich více, každá je uzavřena v závorkách
("(podm1) (podm2) ..."
). Podmínek je několik druhů:
$iterator->odd ? odd : even
– je-li$iterator->odd
vyhodnoceno jako pravdivé, pak se atributu dáodd
, jinakeven
$iterator->first ? first
– je-li$iterator->first
vyhodnoceno jako pravdivé, pak se atributu nastavífirst
, jinak nic$title?
– zkontroluje se, jestli$title
existuje (isset()
), a je-li tomu tak, vrátí se jeho hodnota$title ?: no title
– zkontroluje se, jestli$title
existuje (isset()
), a je-li tomu tak, vrátí se jeho hodnota, jinakno title
Pár příkladů:
<ul>
<li n:foreach="$array as $item" n:[class]="
($iterator->first ? first)
($iterator->last ? last)
($iterator->odd ? odd : even)
">
<span n:[title]="$item['title'] ?: Default title">{$item['name']}</span>
<img src="{$item['image']}" n:(alt)="$item['image_title']?">
</li>
</ul>
- jakubkulhan
- Člen | 55
jasir napsal(a):
Dá se ale nějak zapsat pomocí {} ?
Tohle řeší to jen en:
ka. Nějak si ani neumím představit,
jak by to to mělo vypadat, kdyby se to mělo zapisovat pomocí {
}
. Něco jako:
<ul>
<li n:foreach="$array as $item" {[class] ($iterator->first ? first) ...}>
<span {[title] $item['title'] ?: Default title}>{$item['name']}</span>
<img src="{$item['image']}" {(alt) $item['image_title']?}>
</li>
</ul>
?
To {attr ...}
vypadá líp…
- newPOPE
- Člen | 648
vrana napsal(a):
<li n:class="item, odd => $iterator->odd, last => $iterator->last">
Tohle je podle mě to pravé.
+1
riesenie od jakubkulhan cize zapis na viac riadkov atributu sa mi zda krkolomne, davat bacha na " a pod… to je o hubu a z pohladu kodera asi nemozne. riesenie od Jakuba Vranu povazujem opticky za najlepsie s tym ze si viem predstavit nieco taketo
<?php
<li n:class="item, odd => $iterator->isOdd(), admin => $user->isInRole(...), special => CustomClass::isSpecial()">
?>
v podstate nahod classu ak TRUE
.
Editoval newPOPE (22. 10. 2010 14:57)
- jasir
- Člen | 746
newPOPE napsal(a):
riesenie od jakubkulhan cize zapis na viac riadkov atributu sa mi zda krkolomne
ale to je přeci jen možnost pro přehlednost, psát se to dá do
řádku.
Jinak mě se líbí právě to odlišení maker nette např.
n:foreach
od attributů, tedy
n:(class)
, případně n:[class]
pro nepovinný
atribut. Funguje to pak na všechny atributy které si vymyslím a je to pěkné
a přehledné. Mě se nejvíce líbí právě řešení Jakuba.
Editoval jasir (22. 10. 2010 16:22)
- David Grudl
- Nette Core | 8218
hason napsal(a):
V šablonovacím systému, který používám bych napsal:
Díky za odpověď, je zajímavé se podívat, jak to řeší jiné systémy. Z mého pohledu to je bída, nechci se tím inspirovat a hledám (čti hledáme) řešení jiné. Opakuji tedy zase totéž. Alespoň se můžeš znovu pobavit.
- paranoiq
- Člen | 392
{{['ahoj', ['jsem', 'nejsem']|cycle(i), 'leader', loop.last ? ':)' : '']|filter|join(' ')}}
intuitivní, jednoduché. s takovým šablonovadlem jde práce sama! … ne vážně, tohle bych fakt používat nechtěl
<li n:class="item, odd => $iterator->odd, last => $iterator->last">
super!
- Yrwein
- Člen | 45
Zajímalo by mě, jak by paranoiq přepsal příklad z Twigu do Latte tak, aby to bylo „jednoduché“ a „intuitivní“. // Dobře se to kritizuje šablonovací systém z neintuitivnosti, když ho člověk nezná. :) (Edit: Má omluva, pokud se pletu; hádám.) Na syntaxi Twigu jsem se díval kdysi dávno a až na „cycle“ filter (jo, práskám se do čela, když teď vím, co znamená :]) jsem s tím neměl problém – krom toho, že je to celkem střelená ukázka. :)
___
Abych byl aspoň trochu konstruktivní, hodím pseudokód možného řešení – aneb nestačil by pouhý filter, který by vzal pole?
<li{['odd' => $iterator->isOdd(), 'bla' => $item->isBla()]|class}>
Editoval Yrwein (22. 10. 2010 17:02)
- David Grudl
- Nette Core | 8218
Beru teda za domluvenou následující syntax, ok?
<li n:class="item, odd => $iterator->odd, last => $iterator->last, $selected ? selected : notselected">
- jasir
- Člen | 746
David Grudl napsal(a):
Beru teda za domluvenou následující syntax, ok?
<li n:class="item, odd => $iterator->odd, last => $iterator->last, $selected ? selected : notselected">
Davide, chceš tedy zavést jen makro n:class
, nebo
n:attribut
? Protože pak může dojít ke konfliktu názvů
atributů a třeba uživatelských maker. V případě varianty
n:(atribut)
by k tomu dojít nemělo.
- David Grudl
- Nette Core | 8218
Protože class je nejčastější případ použití, tak udělám
n:class
(a n:href
) a pro ostatní atributy syntaxi
ještě vymyslíme. Nejkonzistentnější by asi
bylo n:attr-xxx
- paranoiq
- Člen | 392
Yrwein napsal(a):
Zajímalo by mě, jak by paranoiq přepsal příklad z Twigu do Latte tak, aby to bylo „jednoduché“ a „intuitivní“.
uvedený příklad třeba tak. (místo „:)“ bych samozřejmě použil nějaký normální název třídy)
<li n:class="ahoj, $selected ? jsem : nejsem, leader, smajlik => $iterator->last">
všimni si, že s navrhovanou syntaxí se obejdu bez cyklování, filtrování a joinování – prostě v šabloně neprogramuju
Dobře se to kritizuje šablonovací systém z neintuitivnosti, když ho člověk nezná. :)
(Edit: Má omluva, pokud se pletu; hádám.) Na syntaxi Twigu jsem se díval kdysi dávno a až na „cycle“ filter (jo, práskám se do čela, když teď vím, co znamená :]) jsem s tím neměl problém – krom toho, že je to celkem střelená ukázka. :)
hádáš správně šablony symfony nadrcené nemám. z ukázky jsem ale pochopil, že funkčnost o které se tu bavíme prostě twig nemá a kodér si jí musí v šabloně vyrábět pokaždé znovu na koleně. takovéhle složitosti do šablon nepatří
David Grudl napsal(a):
Beru teda za domluvenou následující syntax, ok?
<li n:class="item, odd => $iterator->odd, last => $iterator->last, $selected ? selected : notselected">
mohla by se prohodit podmínka a hodnota, aby byla podmínka vždy na začátku. zmizí pak jedno WTF
<li n:class="item, $iterator->odd => odd, $iterator->last => last, $selected ? selected : notselected">
nebo ještě jednodušeji – zahodit šipku a použít otazník. pak už nejsou dvě formy zápisu ale jen jedna u které lze vynechat část za dvojtečkou. pro tuhle verzi hlasuju :]
<li n:class="item, $iterator->odd ? odd, $iterator->last ? last, $selected ? selected : notselected">
Editoval paranoiq (23. 10. 2010 0:30)
- David Grudl
- Nette Core | 8218
Mně se ta šipka a mix (byť pseudo) klíčů s hodnotama taky děsně
nelíbí. Ještě bych se ale zbavil zavádějícího ?:
<li n:class="item, $iterator->odd ? odd, !$iterator->last ? notlast, $selected ? selected : notselected">
- David Grudl
- Nette Core | 8218
Bude i fungovat tohle:
<a n:class="foo, ($iterator->first ? first), ($foo->bar ? bar : baz)">Foo</a>