Nette\Application\AppForm
Třída AppForm je navržena speciálně pro použití v presenteru – má obslužné mechanismy na zpracování signálů. Od třídy Form se liší také tím tím, že má převrácené argumenty v konstruktoru (Form má jako první argument jméno a až jako druhý rodičovský IComponentContainer). Třída Form má parametr $parent uvedený až „bokem“ proto, že tam, kde se používá, obvykle žádný parent není potřeba a ani neexistuje.
AppForm využívá ke svému zpracování handlery. Ty se zpracovávají v metodě prepare{View} a fázi state changing v životním cyklu presenteru, proto je musíme zaregistrovat právě zde.
Metoda onSubmit se volá jen v případě, že je formulář
skutečně odeslán, není potřeba znovu kontrolovat přes
isSubmitted. Maximálně pokud můžeme zjistit, jakým prvkem byl
formulář odeslán. onSubmit se provede pouze pokud byl formulář
úspěšně a validně odeslán.
Obslužné handlery musíme nějak výstižně pojmenovávat, nesmí se nám
zaměňovat název s metodou handle{Signal}. Takovou klasikou bývá
{componentName}_{event}, tedy třeba
loginForm_onSubmit. To ale záleží na uvážení programátora.
Tato metoda také pobírá jeden argument, kterým je instance formuláře.
Obsluha událostí
Pro obsluhu událostí se doporučuje používat následující vzor (viz Best practice: Formulářová tlačítka):
class SomePresenter extends BasePresenter
{
/** @var Form */
public $form;
...
public function prepareDefault()
{
// definice formuláře
$form = new AppForm($this, 'formName');
$form->addText('name', 'Your name:');
$form->addSubmit('ok', 'Send')
->onClick[] = array($this, 'OkClicked'); // nebo 'OkClickHandler'
$form->addSubmit('cancel', 'Cancel')
->setValidationScope(FALSE) // prvek se nebude validovat
->onClick[] = array($this, 'CancelClicked'); // nebo 'CancelClickHandler'
// alternativa:
$form->onSubmit[] = array($this, 'FormSubmitted'); // nebo 'FormSubmitHandler'
if (!$form->isSubmitted()) {
// první zobrazení, nastavíme výchozí hodnoty
$form->setDefaults($defaults);
}
$this->form = $form;
}
// obslužné handlery:
public function OkClicked(SubmitButton $button)
{
// submitted and valid
Debug::dump($form->getValues());
redirect(...);
}
public function CancelClicked(SubmitButton $button)
{
// process cancelled
redirect(...);
}
public function FormSubmitted(Form $form)
{
// manual processing
if ($form['cancel']->isSubmittedBy()) { ... }
}
}
Obslužný handler pro onSubmit lze použít v případě, kdy
formulář nemá žádné nebo právě jedno tlačítko. V odstatních
situacích bývá vhodnější využít handler onClick přímo na
tlačítku.
Handler onClick se volá před handlerem onSubmit.
Handlery se volají pouze v případě, že je odeslání
validní. Uvnitř metody OkClicked tedy není nutné
ověřovat validitu formuláře. Naopak metoda FormSubmitted může být
zavolána i v případě nevalidního formuláře, byl-li odeslán tlačítkem
Cancel.
V případě, že formulář nebyl odeslán tlačítkem (například byl
odeslán přes JavaScript), nebo se tak tváří kvůli chybě v Internet
Exploreru, bude Nette za odesílací tlačítko považovat první tlačítko
formuláře. Tudíž obsluha přes události onClick je
spolehlivá.
Obslušné handlery se vyvolají při prvním volání metody
isSubmitted() nebo processHttpRequest(), při
použití uvnitř Nette\Application vyvolání
zajistí samotný presenter.
Kontrola odeslání formuláře
Použití kontroly odeslání formuláře by mohlo vypadat takto:
if ($form->isSubmitted()) { ... }
Přídklad by nám vrátil objekt implementující rozhraní
ISubmitterControl (nejčastěji objekt SubmitButton)
v případě, že je formulář odeslán nebo FALSE jestliže
není. Nevadí, že vrácená hodnota je objekt, podmínka se vyhodnotí
korektně.
Můžeme se dotázat, přímo byl formulář odeslán přes konkrétní tlačítko:
if ($form['preview']->isSubmittedBy()) { ... }
Tímto způsobem je i možné postihnout případné složitější struktury:
if ($form['subform']['preview']->isSubmittedBy()) { ... }
Validace jednotlivých prvků formuláře
U každého tlačítka je možné nastavit, jestli vyžaduje validaci:
$form->addSubmit('cancel', 'Cancel')
->setValidationScope(FALSE); // nebude se nic validovat
Funkce se jmenuje tak proto, že se plánuje její rozšíření o možnost předat pole nebo skupinu (to zůstává k diskusi) prvků, na které se validace při stisku tlačítka omezí.
Vynechání prvku z vykreslování
$form['save']->setRendered(TRUE);
Tímto zavoláním vynecháme prvek z vykreslování. Formulář ho pak považuje za vykreslený, tudíž ho nevykreslí, a my si jej pak můžeme vykreslit v šabloně ručně. To se hodí v případech, kdy chceme například vykreslit samostatně více odesílacích tlačítek vedle sebe.
Příklad více formulářů na jedné stránce
class SomePresenter extends BasePresenter
{
/** @var Form */
public $form1;
/** @var Form */
public $form2;
...
public function prepareDefault()
{
$form1 = new AppForm($this, 'form1');
// ... definice formuláře 1
$form1->onSubmit[] = array($this, 'form1_onSubmit');
$form2 = new AppForm($this, 'form2');
// ... definice formuláře 2
$form2->onSubmit[] = array($this, 'form2_onSubmit');
$this->form1 = $form1;
$this->form2 = $form2;
}
...
public function form1_onSubmit(AppForm $sender)
{
// Zpracování form1
Debug::dump($sender->getValues());
}
public function form2_onSubmit(AppForm $sender)
{
// Zpracování form2
Debug::dump($sender->getValues());
}
}
Defaultně action formuláře ukazuje na stejný view/scene se stejnými
parametry, jen se do URL přidá signál (?do=form1-submit), který
Nette předá příslušnému formuláři a ten se podle toho zařídí (zavolá
si metody definované v onSubmit). Pokud se má formulář
odesílat do jiného view, tak je potřeba ten signál přidat do
argumentů.
Iterování nad formulářem
K iterování nad formulářem stejně jako nad polem svádí zápisy
$form['name']->... tedy:
foreach ($form as $name => $component) { ... }
což je samozřejmě možné, ale zvažme, že ne každá komponenta
$form je prvek FormControl. Můžou tam být
jakékoliv jiné komponenty, nebo kontainery, které teprve obsahují prvky
formuláře, případně další kontainery atd. Pro iterování je lepší
použít šikovnou metodu $form->getComponents($deep = FALSE, $type =
NULL), kde první parametr říká, zda se má iterovat do hloubky (tj.
projít i prvky konteinerů) a druhý nastavuje volitelný filtr:
foreach ($form->getComponents(TRUE, 'Nette\Forms\IFormControl') as $control) {
$control->setValue(...);
}
Viz také:
