Nette Framework: Neprůstřelné formuláře III

Vzhled formulářů může být velmi pestrý a různorodý. Dnes si ukážeme, jak tuto různorodost pochytit v Nette Framework a na závěr ještě zabrousíme k tématu lokalizace.
Seriál: Začínáme s Nette Framework (17 dílů)
- Nette Framework: Neprůstřelné formuláře II 9. 6. 2009
- Nette Framework: Neprůstřelné formuláře III 16. 6. 2009
- Nette Framework: Cache 23. 6. 2009
Nálepky:
V předchozím díle seriálu o Nette Framework jsme vytvořili kompletní formulář s validačními pravidly. Dnes se podíváme, jak formulář vykreslit.
V praxi můžeme narazit na dva extrémy. Na jedné straně stojí potřeba v aplikaci vykreslovat řadu různých formulářů, které jsou si vizuálně podobné jako vejce vejci. V takovém případě oceníme, když úkony spojené s vykreslením formuláře budou co nejjednodušší. Jde obvykle o případ administračních rozhraní.
Na druhé straně tu jsou rozmanité formuláře, kde platí: co kus, to originál. Jejich podobu nejlépe popíšeme jazykem HTML. A samozřejmě kromě obou zmíněných extrémů narazíme na spoustu formulářů, které se pohybují někde mezi.
Nette Framework se přitom snaží vyhovět všem požadavkům. Jak toho dosahuje?
Renderer
V zájmu variability a čistoty kódu frameworku není vykreslování přímo součástí třídy Form, ale má jej na starosti speciální objekt nazývaný renderer. Ten lze nastavit metodou $form->setRenderer()
. Předá se mu řízení při zavolání metody $form->render()
(nebo alternativně echo $form
).
Pokud žádný renderer nenastavíme, bude použit výchozí vykreslovač NetteFormsConventionalRenderer.
Díky tomu je vzhled formulářů plně ve vašich rukou. Máte celou řadu možností, jak vykreslování ovlivnit:
- využít ConventionalRenderer, který je bohatě konfigurovatelný
- vytvořit vlastní renderer
- třeba tak, že vytvoříte potomka ConventionalRenderer
- napíšete si vlastní renderer na míru
- stáhnete si hotový renderer z internetu
- a nebo vykreslíte formulář manuálně
Začněme u výchozího vykreslovače.
NetteFormsConventionalRenderer
Dovoluje nám vykreslit formulář tou nejsnadnější cestou. Stačí napsat:
echo $form;
a formulář je na světě. Prvky formuláře se vykreslí do HTML tabulky. Výstup vypadá takto:
<table><tr class="required">
<th><label class="required" for="frm-name">Jméno:</label></th>
<td><input type="text" class="text" name="name" id="frm-name" value="" /></td></tr><tr class="required">
<th><label class="required" for="frm-age">Věk:</label></th>
<td><input type="text" class="text" name="age" id="frm-age" value="" /></td></tr><tr>
<th><label>Pohlaví:</label></th>
...
Hezky naformátované, viďte? :-)
Zda použít nebo nepoužít pro kostru formuláře tabulku je sporné a řada webdesignerů preferuje jiný markup. Například definiční seznam. Pokusíme se ConventionalRenderer překonfigurovat tak, aby formulář v podobě seznamu vykreslil. Konfigurace se provádí editací pole $wrappers
. První index vždy představuje oblast a druhý její atribut. Jednotlivé oblasti znázorňuje obrázek:

Některé oblasti z pole $wrappers.
Standardně je skupina prvků controls
obalena tabulkou ( table
), každý pair
představuje řádek tabulky ( tr
) a dvojice label
a control
jsou buňky ( th
a td
). Nyní obalovací elementy změníme. Oblast controls
vložíme do kontejneru dl
, oblast pair
necháme bez kontejneru, label
vložíme do dt
a nakonec control
obalíme značkami dd
:
$renderer = $form->getRenderer();
$renderer->wrappers['controls']['container'] = 'dl';
$renderer->wrappers['pair']['container'] = NULL;
$renderer->wrappers['label']['container'] = 'dt';
$renderer->wrappers['control']['container'] = 'dd';
echo $form;
Výsledkem je tento HTML kód:
<dl>
<dt><label class="required" for="frm-name">Jméno:</label></dt>
<dd><input type="text" class="text" name="name" id="frm-name" value="" /></dd>
<dt><label class="required" for="frm-age">Věk:</label></dt>
<dd><input type="text" class="text" name="age" id="frm-age" value="" /></dd>
<dt><label>Pohlaví:</label></dt>
...
V poli wrappers lze ovlivnit celou řadu dalších atributů:
- přidávat CSS třídy jednotlivým typům formulářových prvků
- rozlišovat CSS třídou liché a sudé řádky
- vizuálně odlišit povinné a volitelné položky
- určovat, zda se chybové zprávy zobrazí přímo u prvků nebo nad formulářem
- atd…
Manuální vykreslování
ConventionalRenderer nám umožňuje bleskově renderovat formuláře se standardním vzhledem. Jak ale vykreslit formulář, který lze jen těžko postihnout polem $wrappers
nebo vlastním rendererem? Asi nejpragmatičtější řešení je takový formulář popsat přímo HTML šablonou.
Jak už víte, jednotlivé prvky formuláře lze adresovat podobně jako prvky pole (tj. třeba $form['name']
). Co možná nevíte, tak že každý prvek disponuje metodami getLabel()
a getControl()
, které vracejí HTML kód popisky a samotného prvku. A jak jsme si ukázali už dříve, Nette Framework dovoluje ke getterům přistupovat podobně, jako by to byly proměnné, takže stačí psát jen label
a control
. Dejme tyto informace dohromady a máme tu manuální renderování:
<?php $form->render('begin') ?>
<?php $form->render('errors') ?>
<table><tr class="required">
<th><?php echo $form['name']->label ?></th>
<td><?php echo $form['name']->control ?></td></tr><tr class="required">
<th><?php echo $form['age']->label ?></th>
<td><?php echo $form['age']->control ?></td></tr><tr>
<th><?php echo $form['gender']->label ?></th>
<td><?php echo $form['gender']->control ?></td></tr><tr>
<th><?php echo $form['email']->label ?></th>
<td><?php echo $form['email']->control ?></td></tr>
...
</table>
<?php $form->render('end') ?>
V tuto chvíli jste naprostým pánem vygenerovaného kódu. Formulář si vykreslíte přesně na míru.
Když do vykreslování zapojíme ještě filtr CurlyBracketsFilter, může výsledná šablona vypadat takto:
<table><tr class="required">
<th>{!$form['name']->label}</th>
<td>{!$form['name']->control}</td></tr>
....
Nyní už máte základní přehled v tom, jak lze v Nette Framework vykreslit formulář.
Automatický překlad formulářů
Pokud programujete multijazyčnou aplikaci, budete nejspíš potřebovat stejný formulář vykreslit v různých jazykových mutacích. Formuláře v Nette Framework disponují podporou pro snadný překlad. Stačí, když formuláři nastavíte tzv. překladač, což je objekt implementující rozhraní NetteITranslator
. Rozhraní má jedinou metodu translate()
.
class MyTranslator extends Object
{
/**
* Translates the given string.
* @param string message
* @param int plural count
* @return string
*/
public function translate($message, $count = NULL)
{
return ...;
}
}
$form->setTranslator($translator);
V tu chvíli se nejen všechny labely, ale i všechny chybové hlášky nebo položky select boxů transparentně přeloží do jiného jazyka.
Jako konkrétní implementaci překladače můžete použít například GNU gettext nebo Zend_Translate.
Autor článku je vývojář na volné noze, specializuje se na návrh a programování moderních webových aplikací. Vyvíjí open-source knihovny Texy, dibi a Nette Framework a pravidelně pořádá školení pro tvůrce webových aplikací, které od podzimu 2009 nabídne kurz vývoje AJAXových aplikací.
Toto mi v příkladu chybí.
Článek není součástí serialu
Pravda, tak ted uz je.
mohl bych si rypnout? proc je formatovani reseno pomoci tabulky a ne cele pomoci
CSS? Doba, kdy se rozlozeni stranky resilo tim, ze se elementy nacpaly do ruzne
vnorenych tabulek, je snad za nami :) Nemely by se tagy
/grr, ted mi to useklo comment po napsani tagu tables./
…tagy tables pouzivat jen pro skutecne tabulky? takhle mi to zrusi validitu
CSS stranky.
Mozes si to predsa lahko zmenit cez $wrappers ako sa pise v clanku:
Standardně je skupina prvků controls obalena tabulkou ( table), každý
pair představuje řádek tabulky ( tr) a dvojice label a control jsou buňky (
th a td). Nyní obalovací elementy změníme. Oblast controls vložíme do
kontejneru dl, oblast pair necháme bez kontejneru, label vložíme do dt a
nakonec control obalíme značkami dd:
a čo to je to „CSS stránka“? a ako „tabulky narušujú validitu“?
vieš o tom xxx ale ideš tu niekoho poučovať… použitie tabuliek v tomto
príapade je úplne opodstatnené…
Jelikož článek je o tom, jak vykreslování a formátování nastavit na
míru, tak otázce moc nerozumím. Ani nevidím jakoukoliv souvislost mezi
„rozložením stránky“ a „formátováním formuláře“. A vlastně
nerozumím ani tomu „zrušení validity“. Prostě nějak nechápu ;)
Jeden formulář přece není celá stránka. A podle určité filozofie (ke
které se hlásím) většina formulářů charakter tabulky skutečně má. Je
to vlastně přehled popisků, jejich hodnot a jejich případných chyb.
Přesně tak – dokonce bych řekl, že použití tabulky u formů je hodně použitelné. Jen tak se totiž v tabulce formátování nerozhodí obzvláště při vypisování různých chybových hlášení a poznámek k inputům.
Ano, hlavně pokud chcete vypisovat chybové hlášky pod každý jednotlivý element tak jsou buňky tabulky příjemnější, jednodušší a řekl bych i lepší.
Myslím, že není důvod mít celou aplikaci bez jediné tabulky – když je vhodný případ.