Zavináčová magie v praxi
- Panda
- Člen | 569
Protože spousta lidí, převážně těch začínajících, má problémy s pochopením a aplikací zavináčové magie, rozhodl jsem se sepsat tento krátký textík, který, doufám, alespoň některé mýty kolem zavináčů kousek poodhalí a mnohým usnadní bádání.
Nejprve krátký „teoretický“ úvod.
Co to vlastně je zavináč v šabloně? K čemu slouží?
Pokud nepoužíváme AJAX, nemá pro nás zavináč žádný význam. S prvním AJAXovým požadavkem ale přichází otázka: co budě vlastně při takovém požadavku potřeba vykreslovat? Na tuto otázku odpovídají snippety a k tomu jim napomáhají zavináče.
Zavináč umožňuje vykonání příkazu v šabloně nezávisle na tom, zda se šablona vykresluje pro normální požadavek, nebo pro AJAXový. Při AJAXovém požadavku šablona bez zavináčů a snippetů nevykreslí vůbec nic – její obsah se v podstatě přeskočí. Pokud přidáme snippet, tak se jeho obsah bude vykreslovat v závislosti na tom, zda je invalidován (Control::invalidateControl), případně zda je invalidována komponenta, které šablona náleží (v takovém případě se tedy vykreslují všechny snippety z šablony).
Každá šablona se překládá do PHP kódu. Všechny struktury a
vymoženosti, které nám poskytuje CurlyBracketsFilter
, lze tedy
zapsat pomocí klasického PHP (ovšem kdo by se s tím psal, že?). Snippet
v podstatě není nic jiného, než podmínka, která kontroluje, zda se
snippet má nebo nemá vykreslit. Ale pokud se při AJAXovém požadavku obsah
šablony v podstatě přeskočí, jak se PHP dostane k podmínce? Pokud je
snippet zapsaný přímo v šabloně, tak to není žádný problém, snippet
si zajistí sám, aby ho bylo vidět. Jak ho ale PHP najde, pokud bude
uzavřený do nějaké podmínky, bloku, cyklu nebo komponenty? Nijak, musí se
mu pomoci. A nyní vstupují na scénu zavináče a vychutnávají si svůj
moment slávy…
Jak již bylo řečeno, zavináče umožní vykonání příkazu nezávisle na tom, zda se jedná o AJAXový požadavek, nebo normální. Příkaz se vykoná vždy. A právě to slouží k tomu, aby se PHP parser „probojoval“ až ke snippetu. V podstatě v tom tedy žádná magie není, stačí dát zavináč kolem všech řídicích struktur, bloků, komponent a vkládání šablon, ve kterých máme snippety. Jednoduché, že?
O generování kódu šablony jsem již psal. Pokud jste tedy zvědaví
na některé podrobnosti o tom, co se děje za
CurlyBracketsFilter
em, podívejte se do jiného vlákna: https://forum.nette.org/…iewtopic.php?….
Kam tedy zavináč patří?
- Před
{include}
, který vkládá obsah se snippety, ať už se jedná o blok nebo šablonu. Jeden takový bývá v šabloně s layoutem, většinou{include $content}
, případně{include #content}
u nových šablon. Zavináč se vždy píše před první složenou závorku:@{include #content}
. - Před vykreslení komponenty, která obsahuje snippety, ať
už pomocí makra
{control ...}
(popř.{control ...}
, které je aliasem pro{widget}
), nebo přímého volání vykreslovací funkce:{!$myControl->render()}
,{!$control['myControl']->render()}
. - Před řídící struktury, ve kterých se nacházejí
snippety. Pokud má struktura více částí, například
{if} ... {else} ... {/if}
, musí se zavináč vložit před každou její část. Zavináče se týkají následujících maker:{if}
,{elseif}
,{else}
,{/if}
,{ifset}
,{elseifset}
,{/ifset}
,{ifCurrent}
,{foreach}
,{/foreach}
,{for}
,{/for}
,{while}
,{/while}
,{continueIf}
,{breakIf}
,{cache}
,{/cache}
. Při vnořování struktur je potřeba vložit zavináč na každou úroveň. - Před definice bloků se snippety:
{block ...} ... {/block}
. Platí stejné pravidlo jako pro řídicí struktury – zavináč patří před všechny části. Pokud se jedná o poslední blok v šabloně a za jeho obsahem už nic není, můžeme ukončení{/block}
vypustit, takže zavináč bude pouze na jeho začátku. - Před příkazy, které nějakým způsobem ovlivňují obsah,
který ve snippetu vykreslujeme. Jedná se například o makra
{assign}
a{default}
, ve kterých nastavujeme proměnné pro snippet. Může se také jednat o PHP kód vložený v{? ...}
nebo volání funkcí s vedlejšími účinky.
Kam naopak zavináč nepatři?
- Před součásti makra
{snippet ...} ... {/snippet}
. Pokud se PHP probojuje až k snippetu, řídí si snippet své vykreslování už sám na základě zmíněné invalidace. - Do obsahu snippetu. Uvedení zavináče ve snippetu může způsobit vygenerování kódu s úplně jinou logikou, než která byla původně zamýšlena.
- Před vykreslení komponent, jejichž vykreslování není založeno
na Nette šablonách. Zavináč před takovým vykreslováním by
způsobil, že se nám komponenta bude vykreslovat vždy. Jednou takovou
komponentou je i
Form
sConventionalRenderer
nebo rendererem od něj odvozeným..
Příklad
Pro názornost jeden lehce komentovaný příklad. Jsou použity nové šablony a jakákoliv podobnost příkladu se skutečností je čistě náhodná.
@layout.phtml
:
{* ... *}
<div id="content">
@{include #content}
</div>
{* ... *}
<presenter>/<action>.phtml
:
{* nastavujeme titulek stránky pro layout
- není potřeba zavináč *}
{assign title => 'Nadpis'}
{* definujeme blok se snippety - @ *}
@{block #content}
{* komponenta používá šablonu se snippety - @ *}
@{control actionList}
{* před snippet zavináč nepatří *}
{snippet info}
{ifset $showInfo}
{* komponenta sice používá šablonu se snippety,
ale uvnitř snippetu nepoužíváme zavináče! *}
{control infoBar}
{/if}
{/snippet}
@{if count($items) > 0}
<ul>
{* vytváříme proměnnou, kterou použijeme ve snippetu - @
v cyklu foreach můžeme sice použít objekt $iterator,
ale toto je pouze demonstrace myšlenky *}
@{assign counter => 0}
@{foreach $items as $item}
@{if $item->visible}
{snippet item$item->id li} {* <li id="item$item->id"> *}
{* každý snippet musí mít jedinečné jméno,
proto do jeho názvu vkládáme $item->id *}
{= ++$counter}: {$item->title}
{snippet} {* </li> *}
@{/if}
@{/foreach}
</ul>
{* v této podmínce nemáme snippety,
zavináče nejsou potřeba *}
{if $orderingAllowed}
<div class="foo">
<a href="{link editPositions}">Upravit pořadí položek</a>
</div>
{/if}
@{/if}
{* formulář s ConventionalRenderer - bez zavináče! *}
{control createNewItemForm}
{* koncový @{/block} vypouštíme *}
Na závěr bych si dovolil jedno malé upozornění: špatné umístění zavináčů může vést k velmi nepříjemným komplikacím, od neškodného nepřekreslování snippetu, přes vykreslení snippetu na samotný začátek stránky až po vygenerování kódu s úplně jinou logikou, než bylo původně zamýšleno. Proto buďte při jejich používání opatrní a pokud si s něčím nebudete jisti, nebojte se zeptat. Minimálně já vždy rád pomohu…
Pokud jsem na něco zapomněl, tak se omlouvám. Ozvěte se – doplníme, upravíme, poladíme.
- JakubKohout
- Člen | 92
No co sem koukal na záznam z (před)poslední soboty o těch šablonách, tak bylo řečeno že je to mrtvá technologie a co sem z toho vyvodil, tak v novejch šablonách by to mělo bejt vyřešený jinak než prerušováním ifu(@) při vykreslování
- Panda
- Člen | 569
Mno nové šablony pravděpodobně ještě nejsou úplně hotové, protože zavináče nám ve zdrojáku stále straší, bez nich AJAX nefunguje a žádnou alternativu jsem nenašel. Pokud jsem něco přehlédl, opravte mě…
- Jakub Šulák
- Člen | 222
Zdravím s pár měsíčním odstupem, bych se rád zeptal, jak to vypadá s odstraněním „zavináčové magie“ z Nette? Je to moje malá noční můra. Mám aplikaci, ke které kodéři píší jen templates. Zavináče jim ale dělají stále problémy…
- Honza Marek
- Člen | 1664
David má silnou motivaci dodělat to před 17. říjnem, aby o zavináčích nemusel přednášet.
A také se podíváme na několik žhavých novinek v Nette Frameworku.
Editoval Honza M. (5. 10. 2009 17:17)
- Matúš Matula
- Člen | 257
ahoj, mam nasledovnu sablonu komponenty
{snippet content}
{if $count > 0}
{foreach $jokes as $joke}
{control rating:thumb $joke->rating, $joke->id, $joke->user_rated}
{/foreach}
{/if}
{/snippet}
sablona subkomponenty rating je obaleny v {snippet}
znacke.
problemom zrejme je, ze sa program nedostane k prekresleniu komponenty rating,
pretoze rodicovsky snippet content
nie je invalidovany. signal
subkomponenty sa spracuje, akurat sa neprekresli obsah.
Podla vyssie uvedeneho navodu, by to malo fungovat, nie?
Vdaka za odpoved