Formuláře v Nette 2.1

před 7 lety od David Grudl     edit

Formuláře byly vždy klíčovou součástí frameworku. Od první verze umožňují vývojářům snadno definovat vstupní prvky, vykreslit je a zpracovávat data vložená uživatelem. Nette 2.1 přináší několik nových funkcí a řeší řadu problémů a omezení z předchozích verzí.

Tento článek se zabývá nejdůležitějšími vylepšeními formulářů:

  • nový nízkoúrovňový přístup k datům formuláře,
  • vylepšené vykreslování pomocí atributů n:
  • nové funkce ve validaci formuláře

Low-level formuláře

Nyní lze používat i prvky, které zapíšeme pouze v šabloně a nepřidáme je do formuláře některou z metod $form->addXyz(). Když například vypisujeme záznamy z databáze a dopředu nevíme, kolik jich bude a jaké budou mít ID, a chceme u každého řádku zobrazit checkbox nebo radio button, stačí jej nakódovat v šabloně:

{foreach $items as $item}
	<p><input type=checkbox name="sel[]" value={$item->id}> {$item->name}</p>
{/foreach}

A po odeslání hodnotu zjistíme:

$values = $form->getHttpData($form::DATA_TEXT, 'sel[]');

kde první parametr je typ elementu (DATA_FILE pro type=file, DATA_LINE pro jednořádkové vstupy jako text, password, email apod. a DATA_TEXT pro všechny ostatní) a druhý parametr sel[] odpovídá HTML atributu name.

Podstatné je, že getHttpData() vrací sanitizovanou hodnotu, v tomto případě to bude vždy pole validních UTF-8 řetězců, ať už se pokusíte serveru podstrčit cokoliv. Jde o obdobu přímé práce s $_POST nebo $_GET avšak s tím podstatným rozdílem, že vždy vrací čistá data, tak, jak jste zvyklí u standardních prvků Nette formulářů.

CheckboxList

Nový prvek pro výběr z více možností je CheckboxList. Stejně jako v případě selectboxů nebo radiolistů kontroluje, zda odeslané hodnoty jsou z těch, které nabízíme:

$form = new Form;
$form->addCheckboxList('colors', 'Favorite colors:', array(
	'r' => 'red',
	'g' => 'green',
	'b' => 'blue',
));

Multiple file upload

Najednou lze uploadovat i více souborů, všimněte si true:

$form = new Form;
$form->addUpload('avatar', 'Picture:', true);

Zároveň formuláře mají integrovanou kontrolu, zda nebyl překročen povolený limit velikosti odesílaných dat.

Nové vykreslovací zbraně

Velmi snadno můžete propojit formulář s existující šablonou. Stačí jen doplnit atributy n:name:

function createComponentSignInForm()
{
	$form = new Form;
	$form->addText('user')->setRequired();
	$form->addPassword('password')->setRequired();
	$form->addSubmit('send');
	return $form;
}
<form n:name=signInForm class=form>
	<p><label n:name=user>Username: <input n:name=user size=20></label>
	<p><label n:name=password>Password: <input n:name=password></label>
	<p><input n:name=send class="btn btn-default">
</form>

Atribut n:name lze používat i s elementy <select>, <button> nebo <textarea>.

Dále můžete vykreslovat prvky jako je RadioList, Checkbox nebo nový CheckList pěkně po jednotlivých HTML elementech. Říká se tomu partial rendering:

{foreach $form[gender]->items as $key => $label}
	<label n:name="gender:$key"><input n:name="gender:$key"> {$label}</label>
{/foreach}

Nebo lze použít klasická makra {input gender:$key} a {label gender:$key}, trik je tom názvu s dvojtečkou.

S tím úzce souvisí i aktualizovaný způsob vykreslování checkboxů a RadioListů. Místo dřívějšího

<label>...</label><input>

se nyní vykreslují v praktičtějším tvaru

<label><input>...</label>

pročež si myslím, že odpadne většina důvodů, proč jste tyto prvky potřebovali vykreslovat po částech.

Zároveň také odpadá nutnost kreslit checkboxy trošku jinak než jiné prvky, tj. myslet na to, aby label byl na správném místě. Metoda getLabel() či makro {label} totiž u checkboxů nyní nevrací nic a getControl() či {input} vrací HTML v onom novém tvaru. Pokud ale potřebujete staré chování, přepněte se do zmíněného partial renderingu přidáním dvojtečky: {label checkbox:} a {input checkbox:}.

Podpora pro Bootstrap

V příkladech najdete ukázky, jak nakonfigurovat vykreslování formulářů pro Twitter Bootstrap 2 a Bootstrap 3.

Chytřejší validátory

Validační pravidla Form::INTEGER, NUMERIC a FLOAT rovnou převádí hodnotu na integer resp. float. A dále pravidlo Form::URL, které akceptuje i řetězec ve tvaru např. nette.org, jej automaticky doplní na plnohodnotné https://nette.org.

Přibyla nová validační pravidla Form::BLANK (prvek nesmí být vyplněn) a Form::NOT_EQUAL.

A v argumentech všech validátorů se můžete dynamicky odkazovat na jiné prvky. Takže třeba tady prvek value musí být v rozmezí určeným aktuálními hodnotami prvků min a max:

$form->addText('min');
$form->addText('max');
$form->addText('value')
	->addRule($form::RANGE, 'from %d to %d', array($form['min'], $form['max']));

Chybové zprávy

Makro {control form} nyní vypisuje chybové zprávy přímo vedle souvisejících prvků a nad formulářem se objeví jen ty, které žádnému prvku nepřiřadíme (tj. když místo $form['name']->addError() použijeme $form->addError()). Je to mnohem uživatelsky příjemnější a doporučuji, abyste stejným způsobem vykreslovali i formuláře manuálně, třeba takto. Pomůže vám metoda $form->getOwnErrors(), která vrací chybové zprávy přiřazené jen k formuláři.

Píšeme vlastní prvky

Výrazného zjednodušení doznala tvorba vlastních formulářových prvků. Podívejte se na příklad DateInput, což je prvek pro zadávání data. Zobrazovat se bude jako trojice políček den, měsíc, rok a z pohledu API bude přijímat a vracet objekt DateTime.

Interně se datum reprezentuje jako trojice privátních proměnných $day, $month, $year, které metoda getValue() převede na požadovaný objekt DateTime (tedy pokud půjde o platné datum) a setValue() naopak vstup do této trojice rozloží. Přičemž by měla kontrolovat validitu vstupu a v případě chyby vyhodit výjimku.

Výjimky naopak nevyhazuje loadHttpData(), která se volá po odeslání formuláře, a hodnoty, které uživatel odeslal, získá metodou getHttpData() a uloží do zmíněné trojice proměnných. Jen pozor, tentokrát mluvím o metodě třídy BaseControl, nikoliv Form. Každopádně i v tomto případě getHttpData() vrací očištěná data.

A nakonec metoda getControl() generuje HTML. Pokud je prvek reprezentován jedním HTML elementem, jeho atribut name určí metoda getHtmlName(). Jenže máme prvky tři, tak za název ještě dolepíme řetězec [day], [month] a [year] (včetně těch hranatých závorek). Stejný postfix pak uvádíme při volání getHttpData() ve zmíněné loadHttpData().

Co když místo obyčejného textového pole budeme chtít vykreslit selectbox? Pak oceníte funkci Nette\Forms\Helpers::createSelectBox(). Prvním parametrem je pole nabízených hodnot, druhým pole HTML atributů elementu <option>. V příkladu uvedené selected? s otazníkem znamená, že atribut se uvede pouze u položky s uvedenou hodnotou. Šlo by také uvést např. 'title:' => array(1 => 'January', 2 => ...) s dvojtečkou, což dává možnost každé položce dát jiný title.

Existuje také obdobná funkce createInputList() pro generování skupin inputů. Té lze jako třetí parametr předat pole HTML atributů pro element label, taktéž podporující otazník a dvojtečku.

Dále autoři nových prvků mohou ocenit dvě nové abstraktní třídy ChoiceControl a MultiChoiceControl.

A co ještě?

Pomocí $control->setOmitted() vyjmete prvek z dat, která vrací $form->getValues(). To se hodí pro různé hesla pro kontrolu, antispamové prvky atd. I všechny prvky, které označíte jako $form->setDisabled(), budou takto vyjmuty.

Vylepšeno bylo togglování, nyní by mělo fungovat přesně podle očekávání. Navíc $form->getToggles() vrátí informaci o viditelnosti všech id.

Metoda setValue() u jednotlivých prvků kontroluje datový typ a dále v případě SelectBoxů a podobně vás nenechá nastavit hodnotu, která v nabízených není.

V HTML atributech data-nette-rules se používá čistý JSON, takže nezapomeňte nasadit aktuální netteForms.js.

A nakonec – u jednotlivých tlačítek můžete omezit seznam prvků, které se mají při odeslání formuláře tímto tlačítkem validovat:

$form->addSubmit('edit')
	->setValidationScope(array($form['name'], $form['password']));