Událost onSuccess se nezavolá

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

Vytvářím si komponentu stepControl, která se bude starat o vícekrokový formulář. V jednom momentě potřebuji v komponentě (stepControl) přidat formuláři událost onSuccess, která mi zajistí přesměrování na další formulář. Událost se přidá (zjištěno pomocí dump($form->onSuccess)), ale nezavolá se. V presenteru ale vše funguje.

Událost přidávám takto:

$form->onSuccess[] = function($form) use ($step){
	$this->presenter->redirect('this', array('step' => $step));
	$stop(); //nezavolá se
};

Editoval Nutelac (10. 11. 2012 11:46)

blacksun
Člen | 177
+
0
-

Co takhle? (bez záruky, neznám okolní kód)

<?php
$_presenter = $this->presenter;

$form->onSuccess[] = function($form) use ($step, $_presenter){
    $_presenter->redirect('this', array('step' => $step));
    $stop(); //nezavolá se
};
?>
Nutelac
Člen | 58
+
0
-

V tom problém nebude, protože používám PHP 5.4.

studna
Člen | 181
+
0
-

Jestli se ti nezavolá jen ten řádek $stop(), tak je to logické. Protože před ním redirectneš, je to stejné jako:

$form->onSuccess[] = function($form) use ($step){
	$this->terminate();
	$stop();
};
Nutelac
Člen | 58
+
0
-

Máte pravdu, vůbec jsem si to neuvědomil. Problém ale je spíše to, že se ani přesměrování neprovede.

Editoval Nutelac (10. 11. 2012 13:42)

vvoody
Člen | 910
+
0
-

Potom nieje dôležitá definícia callbacku ale skorej to kde ho definuješ. Ukáž viac kódu. V tvojom prípade zrejme nieje callback ešte uložený v onSuccess v momente kedy sa spracúvajú signály. Podobný problém

Nutelac
Člen | 58
+
0
-

Už jsme přišel na příčinu, ale nevím jak ji řešit. Vícekrokový formulář tvořím tak, že pro každý krok si vytvořím formulář v presenteru, který poté předávám do komponenty StepControl. Problém nastane při vytváření formuláře, konkrétně v metodě Form::fireEvents(), která se zavolá ještě předtím, než odešlu formulář do komponenty StepControl a která se stará o událost onSuccess, takže už není možné přidávat další události. Je to možné nějak vyřešit, nebo vás napadá úplně jiný způsob jak řešit přesměrování na další krok?

Nutelac
Člen | 58
+
0
-

vvoody napsal(a):

	public function createComponentStep1()
	{
		$form = new Form;
		$form->addText('email');
		$form->addSubmit('submit');
		return $form;
	}

	public function createComponentStep2()
	{
		$form = new Form;
		$form->addText('name');
		$form->addSubmit('submit');
		return $form;
	}

	public function createComponentStepForm()
	{
		$form = new StepControl; //unitř StepControlu nastavuji onSuccess
		$form->addStep($this['step1']);
		$form->addStep($this['step2']);
		return $form;
	}
vvoody
Člen | 910
+
0
-

Ukáž celý StepControl. Továrnička pre tie dva formuláre by mala byt logicky v tej komponente, čim sa korektne naviažu na komponentovú štruktúru (alebo ako to mam nazvať) Nette. Neviem čo s tými formulármi robíš v metóde addStep, preto si chcem pozrieť kód tej komponenty. Keď odošleš niektorý z tých formulárov, aká url adresa je v prehliadači?

Nutelac
Člen | 58
+
0
-

url: /?stepForm-step=step1&do=step1-submit

class StepConrol extends Control
{
	/** @persistent */
	public $step;

	private $steps;

	public function render()
	{
		$this->template->setFile(__DIR__ . '/StepControl.latte');
		$this->template->step = $this->getStep($this->step);
		$this->template->render();
	}

	public function addStep(IContainer $step)
	{
		$this->steps[$step->name] = $step;
	}

	public function getStep($name)
	{
		$step = $this->steps[$name];
		$next = 'step2';
		$step->onSuccess[] = function() use ($next){
			$this->presenter->redirect('this', array('step' => $next));
		};
		return $step;
	}
}
vvoody
Člen | 910
+
0
-

No uvedom si to že StepConrol pri spracovaní formuláru nieje vôbec vytvorený. StepControl je vyžadovaný až počas rendrovania keď voláš {control StepForm} čiže naviazanie callbackov je vykonané až počas rendrovania šablóny. Jedna možnosť je zabespečiť aby bol StepConrol inicializovaný počas inicializácie jednotlivých formulárov.

public function createComponentStep1()
{
    $form = new Form;
    $form->addText('email');
    $form->addSubmit('submit');
    $this['stepForm'];
    return $form;
}

public function createComponentStep2()
{
    $form = new Form;
    $form->addText('name');
    $form->addSubmit('submit');
    $this['stepForm'];
    return $form;
}

public function createComponentStepForm()
{
    $form = new StepControl; //unitř StepControlu nastavuji onSuccess
    $form->addStep($this['step1']);
    $form->addStep($this['step2']);
    return $form;
}

čo je len škaredý hack, ktorým ti chcem ujasniť prečo ti to nejde. (edit: Nie radšej to ani neskúšaj :D to by skoncilo zrejme nekonecnym cyklom.) Druhá a správna (a oveľa logickejšia) možnosť je mať továrničky formulárov v StepControl. V takomto prípade signál odoslaného formulára najskorej putuje do StepControl, čím vynúti jeho vytvorenie a až potom do konkrétneho formuláru.

Editoval vvoody (10. 11. 2012 15:23)

Nutelac
Člen | 58
+
0
-

Pomůže, pokud za onSuccess přidám

$step->fireEvents();

Nevím ale, jestli je vhodné tuto metodu volat a navíc při přesměrování nette vyhodí Nette\Application\AbortException

blacksun
Člen | 177
+
0
-

Jak psal vvoody, máš vytváření jednotlivých formulářů pro kroky uvnitř té StepControl třídy?

Nutelac
Člen | 58
+
0
-

Myslíte to, že bych přesunul vytváření formulářů (metody createComponentStep*) do StepControlu? Něco jako:

public function createComponentStepForm()
{
	$form = new StepControl;
	$form->addStep('step1')
		->addText('email');
	$form->addStep('step2')
		->addText('name');
	return $form;
}

Editoval Nutelac (10. 11. 2012 15:57)

blacksun
Člen | 177
+
0
-

Naopak.

<?php
class StepConrol extends Control
{
    /** @persistent */
    public $step;

    private $steps;

    public function render()
    {
        $this->template->setFile(__DIR__ . '/StepControl.latte');
        $this->template->step = $this->getStep($this->step);
        $this->template->render();
    }

    public function addStep(IContainer $step)
    {
        $this->steps[$step->name] = $step;
    }

    public function getStep($name)
    {
        $step = $this->steps[$name];
        $next = 'step2';
        $step->onSuccess[] = function() use ($next){
            $this->presenter->redirect('this', array('step' => $next));
        };
        return $step;
    }

    public function createComponentStep1()
    {
        $form = new Form;
        $form->addText('email');
        $form->addSubmit('submit');
        return $form;
    }

    public function createComponentStep2()
    {
        $form = new Form;
        $form->addText('name');
        $form->addSubmit('submit');
        return $form;
    }

    .....
}
?>
Nutelac
Člen | 58
+
0
-

Tomu jsem se právě snažil vyhnout, protože mi to zničí znovupoužitelnost StepControlu.

Nutelac
Člen | 58
+
0
-

Díky, ale v tvém odkazu se neřeší automatické přesměrování na další krok.

vvoody
Člen | 910
+
0
-

Ako môžeš považovať ten tvoj návrh znovu použiteľný, keď tam máš $next = ‚step2‘;?

Nutelac
Člen | 58
+
0
-

To je prozatímní, než to implementuji.

22
Člen | 1478
+
0
-

Nutelac napsal(a):

Díky, ale v tvém odkazu se neřeší automatické přesměrování na další krok.

Co? Si to prohldni líp asi teda..

Nutelac
Člen | 58
+
0
-

Prohlížím podruhé, ale máte tam jen menu :). Automatické přesměrování na další krok, po kliknutí na submit tam neřešíte.

mildabre
Člen | 62
+
0
-

Nutelac napsal(a):

Vytvářím si komponentu stepControl, která se bude starat o vícekrokový formulář. V jednom momentě potřebuji v komponentě (stepControl) přidat formuláři událost onSuccess, která mi zajistí přesměrování na další formulář. Událost se přidá (zjištěno pomocí dump($form->onSuccess)), ale nezavolá se. V presenteru ale vše funguje.

Událost přidávám takto:

$form->onSuccess[] = function($form) use ($step){
	$this->presenter->redirect('this', array('step' => $step));
	$stop(); //nezavolá se
};

Projel jsem si celou tu diskusi a napadlo mne, zda je to z analytického hlediska vůbec dobrý nápad si vyrábět obecnou komponentu pro obsluhu vícekrokového formuláře. Nevím přesně jak má takovýto vícekrokový formulář vypadat a jakým způsobem zpracovává data, ale…

Po mnoha pokusech a variantách jsem dospěl k názoru, že složité formuláře uživatelé nemilují a radši vyplňují „vícekrokové“ formuláře kde se soustředí v každém kroku vždy na část problematiky. Otázkou je plná kontrola uživatele nad pohybem na krocích a data se nesmí ztratit. Já to řeším tak, že jednotlivé formuláře jsou nezávislé komponenty a mají tlačítko vpřed a zpět (nebo pokračovat a zpět). Data jednotlivých formulářů se ukládají do objektu v SESSION aby byly přístupné i mezi jednotlivými http requesty. Úplně na konci uživatel obvykle vše potvrdí a následuje uložení dat z objektu v SESSION do databáze. Tlačítko vpřed vykoná uložení dat formuláře a přesměrování na další formulář, tlačítko zpět přesměruje na předchozí formulář.

Má smysl si pro takto jednoduchý koncept vytvářet nějakou stepKomponentu ? Tím si jenom zkomplikuješ život. Ostatní Ti zde radí jak tuto šílenost naprogramovat, ale já radím jít na to jednoduchou a přímou cestou klasickými formuláři. Takovéto všeobalující komponenty IMHO Ti nic nezjednoduší. Ale třeba se mýlím, musel bych znát detaily Tvého formuláře.

Filip Procházka
Moderator | 4668
+
0
-

mildabre napsal(a):

Má smysl si pro takto jednoduchý koncept vytvářet nějakou stepKomponentu ? Tím si jenom zkomplikuješ život.

Krokové formuláře dělám přesně jak popisuješ, ale…

Udělat si obecnou komponentu, která se bude starat o ukládání dat z formuláře do session automaticky a která ti bude moct validovat vazby mezi kroky je velice pohodlné.

Problém je, že každá aplikace má svá specifika (jiný způsob procházení tohoto formuláře) a ještě jsem nevymyslel, jaké je ideální api pro takovou komponentu. Vždy ji píši znovu.

Ovšem je to pár řádků a díky tomu že to vyčlením, tak to nemusím psát několikrát v jedné aplikaci ;)