Lepší detekce zrušení formuláře

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Lawondyss
Člen | 106
+
0
-

Mám komponentu

use Nette\Application\UI;
class ExampleForm extends UI\Control
{
  public $onSuccess = array();

  public function createComponentForm()
  {
    $form = new UI\Form;
    $form->onSuccess[] = $this->processForm;

    $form->addText('example', 'Example text')

    $form->addSubmit('save', 'Save');
    $form->addSubmit('cancel', 'Cancel')
      ->setValidationScope(false);

    return $form;
  }

  public function processForm(UI\Form $form)
  {
    if (!$form['cancel']->isSubmittedBy()) {
        $values = $form->getValues();
        dump($values);
    }

    $this->onSuccess($form);
  }
}

kterou používám v presenteru

class ExamplePresenter extends UI\Presenter
{
  public function createComponentExampleForm()
  {
    $form = $this->exampleFormFactory->create();

    $form->onSuccess[] = function (UI\Form $form) {
      if ($form->isValid()) {
        if (!$form['cancel']->isSubmittedBy()) {
          $this->flashMessage('Success!', 'success');
        }
        $this->redirect('default');
      }
    };

    return $form;
  }
}

Trápí mě to, že v presenteru musím znát jméno tlačítka cancel, protože na něm ověřuji, zdali byl formulář odeslán přes něj. Na druhou stranu následné kroky (hláška, přesměrování) do formuláře cpát nechci.

Je nějaký lepší způsob, jak tohle řešit?

David Kudera
Člen | 455
+
+2
-

Co třeba udělat si vedle onSuccess události ještě onCancel, v tom formu podle toho upravit volání a v presenteru poslouchat na obě události?

hranicka
Člen | 23
+
+2
-

Jestli jsem tě správně pochopil, tak chceš z vnějšku té Control reagovat na chování toho formuláře.
Můžeš přidal ještě další události:

/** @var callable */
public $onSuccess = [];

/** @var callable */
public $onCancel = [];

protected function createComponentForm()
{
	// ...

	$form->addSubmit('cancel', 'Cancel')
		->setValidationScope(FALSE);

	$form->onSuccess[] = $this->formSuccess;
}

public function formSuccess(Form $form)
{
	if ($form['cancel']->isSubmittedBy()) {
		$this->onCancel($form);
	} else {
		$this->onSuccess($form);
	}
}

Pak bys mohl v tom presenteru jen navěsit callbacky na události onSuccess a onCancel té Control (nikoliv formuláře):

  • onSuccess by volalo jen v případě úspěšného formuláře, který nebyl odeslán tlačítkem „cancel“
  • onCancel by se volalo v případě, že by byl odeslán tlačítkem „cancel“

Editoval hranicka (1. 7. 2014 21:25)

Šaman
Člen | 2666
+
+1
-

Můžeš taky navěsit obslužné metody přímo na tlačítka, viz dokumentaci

Lawondyss
Člen | 106
+
0
-

@Šaman To právě nemůžu. Formulář nemá vědět, co s ním bude, až dokončí svou činnost.
@DavidKudera + @hranicka Není to sice úplně ono, ale jestli se ve formsech neobjeví addCancel a isCanceled, tak nic lepšího asi nebude. Každopádně díky za tip.

Šaman
Člen | 2666
+
+3
-

@Lawondyss: Proč? On to stejně ví, bude s ním tohle: $form->onSuccess[] = $this->processForm;
Tak proč by rovnou nemohl říct, že každé tlačítko se bude obsluhovat jinou metodou?

<?php
$form->addSubmit('ok', 'Ok')
	->onClick[] = $this->processForm;

$form->addSubmit('cancel', 'Cancel')
	->setValidationScope(FALSE)
	->onClick[] = $this->cancelForm;
?>

Samozřejmě si můžeš připravit formulář bez tlačítek a tohle přidat až v továrničce, pokud chceš formulář obsluhovat jen zvenku.

Editoval Šaman (2. 7. 2014 0:19)

Lawondyss
Člen | 106
+
-3
-

@šaman Jenže proč by měl formulář vědět, kam následně uživatele přesměrovat. To odporuje myšlence znovupoužitelnosti. Takže formulář nechám něco uložit a následně v presenteru nadefinuju, co se má po uložení stát.

besanek
Člen | 128
+
+3
-

A kdo říká že by měl přesměrovávat? Budeš mít dvě události onSuccess a onCancel

$form->addSubmit('ok', 'Ok')
	->onClick[] = function($form) {
		$this->onSuccess($form);
	};

$form->addSubmit('cancel', 'Cancel')
    ->setValidationScope(FALSE)
    ->onClick[] = function(UI\Form $form) {
		$this->onCancel($form);
	};

A pak v presenteru použiješ.

public function createComponentExampleForm()
{
	$form = $this->exampleFormFactory->create();

	$form->onSuccess[] = function (UI\Form $form) {
		$this->flashMessage('Success!', 'success');
		$this->redirect('default');
	};

	$form->onCancel[] = function (UI\Form $form) {
		$this->redirect('cancel');
	};

    return $form;
  }
MartinitCZ
Člen | 580
+
0
-

Proč si to tak stěžuješ?
Já ve většině případů mám funkce zpracování přímo u formuláře a nikdy stím nebyl problém.
Naopak se tím většině problémů vyhneš.

Šaman
Člen | 2666
+
0
-

Lawondyss napsal(a):

@šaman Jenže proč by měl formulář vědět, kam následně uživatele přesměrovat. To odporuje myšlence znovupoužitelnosti. Takže formulář nechám něco uložit a následně v presenteru nadefinuju, co se má po uložení stát.

Chápu, ale:

  1. Dokud jsi používal jednu metodu processForm(), tak jsi narážel na stejný problém, ne?
  2. Obslužné metody se dají vrstvit, proto se přidávají do pole. Takže ty můžeš mít obsluhu částečně ve formuláři i v presenteru. Kdysi jsem to tak používal. Dokonce jsem si ve formuláři zjišťoval, jestli je tato obslužná metoda poslední a pokud ano, prováděl jsem redirect('this'), pokud za ní následovala nějaká další (ta presenterová), nechal jsem redirect na ni. Z presenteru dokonce můžeš pole událostí úplně vyčistit a navěsit tam jen vlastní obsluhu.

P.S. Jen pozor na to, že obslužné události navěšené na tlačítko nedostávají jako parametr Form, ale SubmitButton. Resp. ve starším Nette tomu tak bylo a poslední rok, dva jsem to nepoužil.

<?php
public function onSuccess(\Nette\Forms\Controls\SubmitButton $button)
{
    $form = $button->form;
    // ...
}
?>
MartinitCZ
Člen | 580
+
+1
-

Jen dodám, že v Nette 2.2 je v dané funkci ještě druhý parameter $values. ;)

<?php
public function onSuccess(\Nette\Forms\Controls\SubmitButton $button, $values)
{
    $form = $button->form;
    // ...
    // $values instead of $form->getValues();
}