Formulare: jak bez znevalidneni formulare neprovest onSubmit, zrusit fallback na prvni submit tlacitko
- honza.trtik
- Člen | 10
Scenar s prikladem, o kterem jsme se bavili na posledni sobote po uxcampu:
Mame formular, ve kterem budeme chtit specialni SubmitButton, ktery bude pridavat do formulare nove prvky. Neni problem si k tlacitku zaregistrovat handler pro udalost onClick, ktery do formulare dalsi prvky prida, ale v takovem pripade jiz nebudeme chtit, aby doslo k provedeni formularove udalosti onSubmit. Prisli jsme na dva zpusoby jak toho docilit, ani jeden z nich se nam nezda idealni:
- formular / nejaky z form. prvku invalidovat zavolanim metody addError
- v handleru onClick vynulovat (nastavit na prazdne pole) zaregistrovane handlery pro formularovou udalost onSubmit
prvni pripad neni semanticky spravne, protoze formular v podstate nevalidni neni – jen se nejakym zpusobem zmenil jeho stav, druhy asi neni uplne cisty, protoze sahame nekam, kam bychom zrejme nemeli…
Dale pripominam, ze jsme se bavili o tom, jestli by nestalo za to zmenit soucasne chovani formularu – v krajnim pripade se za tlacitko, ktere odeslalo formular povazovano prvni nalezene.
V souvislosti s timto prikladem me jeste napadlo, jestli by se nemohla k formularum pridat udalost onAnchored, ktera by se volala ve chvili, kdy by bylo mozne pouzit metodu getHttpData (bez rozdilu, zda jde o Form nebo AppForm)
Nasleduje kod, na kterem je situace zhruba popsana, radi se dozvime jak problem resi ostatni.
class TestItem extends TextInput
{
private $removeButton;
public function __construct()
{
parent::__construct();
}
/**
* Vraci textovou reprezentaci cesty k formularovemu prvku se suffixem _removeButton
* @return string
*/
public function getRemoveButtonName()
{
return str_replace(self::NAME_SEPARATOR, '_', $this->lookupPath('\Nette\Form', TRUE)) . '_removeButton';
}
protected function attached($form)
{
parent::attached($form);
if ($form instanceof Form)
{
// Vytvorime tlacitko pro odebrani prvku
$removeButton = new SubmitButton('Odeber');
$removeButton->onClick[] = array($this, 'onClickRemoveButton');
$removeButton->onInvalidClick[] = array($this, 'onClickRemoveButton');
$removeButton->setValidationScope(NULL);
// Pripojime tlacitko na formular
$form->addComponent($removeButton, $this->getRemoveButtonName());
$this->removeButton = $removeButton;
}
}
protected function detached($form)
{
if ($form instanceof Form)
{
// Odpojime tlacitko z formu
$form->removeComponent($this->removeButton);
}
parent::detached($form);
}
public function onClickRemoveButton(SubmitButton $removeButton)
{
$form = $this->getForm();
$this->getParent()->removeComponent($this);
// Na tomto miste bych rad zabranil onSubmitu formulare
// Bud muzu nastavit chybu, to ale neni semanticky spravne
$form->addError('Polozka byla odebrana.');
// Nebo vynuluju handlery onSubmit na formulari - to je trochu hack
$form->onSubmit = array();
}
public function getControl()
{
$form = $this->getForm();
return Html::el('div')
->add(parent::getControl())
->add($form[$this->getRemoveButtonName()]->getControl());
}
}
class TestForm extends AppForm
{
const CONTAINER_NAME = 'container';
const ITEM_DESC_NAME = 'itemDesc';
public function __construct(IComponentContainer $parent = NULL, $name = NULL)
{
parent::__construct($parent, $name);
$this->setUp();
}
protected function attached($presenter)
{
parent::attached($presenter);
// Odeslana data mam u AppForm k dispozici az po pripojeni k presenteru
if ($presenter instanceof Presenter)
{
// Nebylo by lepsi mit moznost pouzit napr callback onAnchored?
$data = $this->getHttpData();
// Pridame polozky do konejneru
if ($this->isSubmitted() && isset($data[self::CONTAINER_NAME]))
{
foreach($data[self::CONTAINER_NAME] as $name => $value)
{
$testItem = new TestItem();
$this[self::CONTAINER_NAME]->addComponent($testItem, $name);
}
}
}
}
protected function setUp()
{
$this->addContainer(self::CONTAINER_NAME);
$this->addText(self::ITEM_DESC_NAME, 'Hodnota pridane polozky');
$addButton = $this->addSubmit('addItem1', 'Pridej polozku');
$addButton->setValidationScope(NULL);
$addButton->onClick[] = array($this, 'onClickAddButton');
$addButton->onInvalidClick[] = array($this, 'onClickAddButton');
$this->addSubmit('send', 'Odeslat formular');
$this->onSubmit[] = array($this, 'onSubmitForm');
$this->onInvalidSubmit[] = array($this, 'onInvalidSubmitForm');
}
public function onSubmitForm(Form $form)
{
die('onSubmit');
}
public function onInvalidSubmitForm(Form $form)
{
die('onInvalidSubmit');
}
public function onClickAddButton(SubmitButton $addButton)
{
$testItem = new TestItem();
// Kolik mame polozek TestItem v kontejneru?
$count = count($this[self::CONTAINER_NAME]->getComponents(FALSE, 'TestItem'));
// Pridame polozku do kontejneru
$this[self::CONTAINER_NAME]->addComponent($testItem, $count);
// Nastavime hodnotu
$testItem->setValue($this[self::ITEM_DESC_NAME]->getValue());
// Vynulujeme hodnotu pole pro zadani hodnoty nove vytvareneho prvku
$this[self::ITEM_DESC_NAME]->setValue(NULL);
// Na tomto miste bych rad zabranil onSubmitu formulare
// Bud muzu nastavit chybu, to ale neni semanticky spravne
$addButton->addError('Polozka byla pridana.');
// Nebo vynuluju handlery onSubmit na formulari - to je trochu hack
$this->onSubmit = array();
}
}
- redhead
- Člen | 1313
Co se týče toho prvního, tak v onSubmit metodě můžeš kontrolovat „neodeslanost“ tím přidávacím tlačítkem:
public function onFormSubmitted($form)
{
if(!$form['pridavaci_tlacitko']->isSubmittedBy())
{
//vykonani pri odeslani
}
}
Takhle se vyhneš vykonáním příkazů při submit..
- honza.trtik
- Člen | 10
redhead napsal(a):
Co se týče toho prvního, tak v onSubmit metodě můžeš kontrolovat „neodeslanost“ tím přidávacím tlačítkem:
public function onFormSubmitted($form) { if(!$form['pridavaci_tlacitko']->isSubmittedBy()) { //vykonani pri odeslani } }
Takhle se vyhneš vykonáním příkazů při submit..
To ale neni reseni, pokud chci vytvorit znovupouzitelnou komponentu, ktera nezavisi na konkretnim formulari (viz TestControl::onClickRemoveButton v prikladu). Na formulari navic muze byt zaregistrovan libovolny pocet handleru onSubmit, ktere nemusim mit vzdy plne pod kontrolou…
- mlha
- Člen | 58
Mě osobně se v Nette velmi nelíbi ten fallback na první
submit button.
Při odeslání formuláře na straně prohlížeče to chápu.
V PHP bych ale měl vždy vědět zda byl form odeslán tím kterým
tlačítkem.
Příklad:
- Odešlu formulář tlačítkem A,
V PHP kdy se formulář vytváři:
- vytvořím tlačítko A
- test zda bylo tl. A použito – aktuálně tento test vždy projde, protože je tl. A jediné
- konstrukce další části formuláře s tlačítkem B
Nově by tedy mohla nastat situace, že formulář tvrdí, že je odeslán, ale neví o submitu, kterým se tak stalo (odesílací submit nebyl přidán do formu). Ale toto mi přijde zcela v pořádku.
Editoval mlha (12. 10. 2010 12:13)