BadSignalException po odoslaní formulára

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

Zdarec,

tentoraz sa mi prihodila dosť podivná chyba… form vykreslí bez problémov, avšak po jeho odoslaní hlási chybu: Nette\Application\UI\BadSignalException #403 – The signal receiver component ‚payForm‘ is not found.

payment.latte

{form payForm}
	{label amount /} {input amount} {$data->currency}
	{input save}
{/form}

presenter

	/**
	 * Pay form
	 * @return Nette\Application\UI\Form
	 */
	public function createComponentPayForm(){
		$form = new Form;

		$form->addText('amount', 'Suma')
			->addRule(Form::FILLED, 'Musíte zadať sumu');
		$form->setDefaults($this->data);

		$form->addSubmit('save', 'Pridať');
		// $form->onSuccess[] = callback($this, 'payFormSubmitted');

		return $form;
	}

	public function payFormSubmitted($form){
		// dump($form->values);
		// exit;
		// $this->getModel('Invoice')->addPay($form->values->amount);
		$this->flashMessage('Faktúra bola úspešne vytvorená','success');
		// $this->redirect('Fakturacia:faktura', $id);
	}

okrem neho je v presenteri ešte 6 komponentov

mkoubik
Člen | 728
+
0
-

A máš něco v příslušné action*() metodě nebo ve startup()?

DOBss
Člen | 24
+
0
-

je tam

public function renderPayment($id){
		$this->template->data = $this->data = $this->getModel('Invoice')->getTotal($id);
	}

a v BasePresenteri je

public function startup(){
		parent::startup();

		$user = $this->getUser();
		if((!$user->isLoggedIn() || !$user->isInRole('admin')) && $this->name!='Admin:Log')
			$this->redirect("Log:in");
		else
			$user->setExpiration(10800, FALSE);
	}

EDIT – neni nejaký limit na počet elementov v komponentoch? či na počet komponentov? (keďže sú tam 2 dosť veľké formy a 2 datagridy)

EDIT2 – taktiež som skúšal meniť názov formy, bez úspechu

Editoval DOBss (5. 12. 2012 21:27)

pepakriz
Člen | 246
+
0
-

Problém bude v tom, že při odeslání formuláře se komponenta vytváří ještě před naplněním $this->data, takže továrnička zhavaruje.

mkoubik
Člen | 728
+
0
-

Jinými slovy, $this->data musíš naplnit už v actionPayment().

DOBss
Člen | 24
+
0
-

thanks, už je to ok – by ma určite nenapadlo, že nette sa bude sťažovať na request, keď nemá k dispozícií dáta pre default…

mildabre
Člen | 62
+
0
-

Máš to sice tzv. „OK“, už Ti to funguje, ale myslím IMHO, že ten koncept jak ten formulář konstruuješ je špatně. Prostě se mě tam něco nezdá, ale co?

  • proč cpeš data z databáze do formuláře již v továrničce?
  • proč používáš na nacpání dat metodu setDefaults() a ne raději metodu setValues() ?

Metodu setDefaults bych doporučil IMHO použít jenom na konstantní hodnoty, na nasosání dat z databáze použij metodu setValues(), ale proboha ne v továrničce, anýbrž v metodě render<Action>(). Ono setDefaults() také bude fungovat, ale korektnější je použít setValues().

Nyní analýza jak formulář pracuje s daty:

  • formulář se vykreslí a zobrazí data z databáze, potřebujeme tedy v netodě render<Action>() nasosat tyto data do formuláře
  • po POSTnutí formuláře se událost onSuccess zpracuje po metodě action<Action>() a před metodou render<Action>(), protože se v ovladači onSuccess provede na závěr přesměrování, metoda render<Action>() se ani nespustí, musíš tedy data sosat v metodě action<Action>(), zde ale pozor, data jsme přece do formuláře nasosli ještě před odesláním a v ovladači onSuccess jsou k dispozici tato data v modifikované podobě, proč je tedy sosat podruhé – zbytečné že ?
  • formulář bude jak pracovat s existujícími záznamy z databáze, tak bude také vytvářet nové záznamy, já to řeší tak, že buďto znám id záznamu – potom provádím UPDATE, nebo neznám ID záznamu, potom provádím INSERT

Více podrobností k tomu, jak formulář řeším já:

  • klíčové pro chování formuláře je to, zda je k dispozici parametr id záznamu tabulky, pokud není NULL tahám data z databáze v metodě render<Action>(), je-li NULL nechám formulář prázdný
  • odeslání formuláře obsluhuji v události onSuccess – je-li id NULL provádím INSERT a přesměruji na stejnou akci + přidám id nově uloženého záznamu, není-li NULL provedu UPDATE záznamu s tímto id a přesměruji na stejnou akci a stejné id
  • protože jsme na konci ovladače události onSuccess přesměrovali, nespustí se nám metoda render<Action>()
  • protože v odeslaném formuláři jsou data z databáze irelevantní tak je nesosáme a metodu action<Action>() tedy vůbec nepotřebujeme
  • po přesměrování se normálně vyvolá metoda render<Action>() a zobrazí se již aktualizovaný/uložený záznam nasosaný z databáze

Myslím, že to je korektní a logický postup a prosím ostatní kolegy zda by se k této důležité problematice formuláře vyjádřili.

Samotný kód je myslím jednoduchý, proto ho nepřikládám, jde zde spíše o analýzu.

enumag
Člen | 2118
+
0
-

Metodu setDefaults bych doporučil IMHO použít jenom na konstantní hodnoty, na nasosání dat z databáze použij metodu setValues(), ale proboha ne v továrničce, anýbrž v metodě render<Action>(). Ono setDefaults() také bude fungovat, ale korektnější je použít setValues().

S tím musím důrazně nesouhlasit. Metoda setDefaults slouží k nastavení výchozích hodnot, setValues k jejich vynucení. Ty to obcházíš tadytím:

protože v odeslaném formuláři jsou data z databáze irelevantní tak je nesosáme a metodu action<Action>() tedy vůbec nepotřebujeme

Takto je to sice v pořádku, výsledný formulář bude stejný jako při volání setDefaults za téměř všech okolností. Problém budeš mít v případě, že z nějakého důvodu máš nějaké pole ve formuláři disabled. Tvůj způsob použití setValues bude mít takové pole naplněné daty z databáze pouze při prvním volání což je bug. Při použití setDefaults tam budeš mít ta data vždy.

EDIT: Kromě toho si dokážu ve formuláři představit tlačítko na obnovení výchozích hodnot. U takového tlačítka bych očekával že mi do všech prvků formuláře vrátí data, která jsou uložená v databázi. Tj. data, která jsem nastavil přes setDefaults. V tvém případě tohle bude neřešitelné.

klíčové pro chování formuláře je to, zda je k dispozici parametr id záznamu tabulky, pokud není NULL tahám data z databáze v metodě render<Action>(), je-li NULL nechám formulář prázdný

Osobně mám raději oddělené akce add (bez parametru) a edit (s vyžadovaným parametrem $id), které obě využívají stejnou továrnu pro formulář.

Editoval enumag (8. 12. 2012 7:50)

mildabre
Člen | 62
+
0
-

@enumag: Díky za konstruktivní opozici, zamyslel jsem se a možná také přejdu na koncept dvou action add + edit se společnou továrnou.

Jaký je rozdíl mezi nastavením a vynucením hodnot formuláře není to jen slovíčkaření ? Analyticky bych viděl rozdíl mezi daty které přednastavujeme do nového záznamu – tyto data podstrčí aplikace a mezi daty z databáze do editovaného záznamu – tyto data pocházejí od uživatelů. Tím že bych pro startovací nastavení použil setDefaults() a pro pozdější setValues() bych odlišil charakter těch dat. V dokumentaci jsem nenašel kdy použít setDefaults() a kdy setValues(), kdy použít kterou jsem odhadnul z názvu těch metod. Navíc bych si tipnul že u běžně zpracovávaného formuláře bude asi úplně jedno kterou z těch metod použijeme.

Nicméně rozdíly mezi setDefaults() a setValues() nějaké jsou – mrkni se do zdrojového kódu, ty metody mají společné jádro a liší se nějakým obalem která řeší zda je formulář někam připojen apod. Kdyby jsi mohl více objasnit o co tam jde by bylo skvělé, tomu už já nerozumím.

Pokud se nějaký prvek ve formuláři disabluje, tak jeho value zůstane beze změny a nedá se editovat. Řekl bych, že při opakované validaci formuláře původní data z databáze nepotřebujeme – musíme pracovat s daty z prohlížeče.

Reset formuláře a návrat k původním hodnotám je subrequest, který bych řešil handlerem na onClick[]-u resetovacího tlačítka které by si pro data do databáze šáhlo, nebo by mohlo formulář přesměrovat na výchozí akci – tím by se také natáhly původní data.

mildabre
Člen | 62
+
0
-

@enumag:
ještě si časem vyzkouším jak se pracuje s disablovaným prvkem formuláře, co kdyby jsi měl pravdu ? Napadlo mne že jak má Nette validaci javascriptem tak se ani pořádně nedá otestovat co se děje, když se validuje na serveru. Jak používám HTML 5 tak i když vypnu Javascript tak to HTML 5 validuje místo javascriptu a na serverovou ani nedojde.

Nevíš náhodou jak jednoduše otestovat serverovou validaci ale ne tak, abych si musel nainstalovat prohlížeč nepodporující HTML 5 ?

Editoval mildabre (13. 12. 2012 2:26)

jiri.pudil
Nette Blogger | 1032
+
0
-

mildabre:
Kdyby jsi mohl více objasnit o co tam jde by bylo skvělé, tomu už já nerozumím.

V tomhle kontextu je v setDefaults důležitá tahle část podmínky:

if (... || !$form->isSubmitted()) {

Tzn. pokud uživatel ve formuláři odeslal nějaká změněná data, setValues mu je přepíše pod rukama zpátky na původní; setDefaults ne.

enumag
Člen | 2118
+
0
-

@mildabre: Koukám, že se nám z toho klube zajímavá diskuse. :-)

V prvé řadě se ti musím omluvit za mystifikaci. Používám to trochu jinak než jak jsem popsal a tak jak jsem to popsal by to ani nefungovalo.

Základní rozdíl mezi setDefaults a setValues (u formuláře), respektive setDefaultValue a setValue (u konkrétního prvku) je ta, že ty default varianty se aplikují Pouze pokud formulář ještě není připojen k presenteru – což je pouze v továrničce, nebo nebyl odeslán. Tím, že setDefaults volám až v action jsem způsobil, že po odeslání už nemá význam a data se z databáze opravdu tahají zbytečně. Tedy jen v jistém slovasmyslu – část těch dat stejně používám někde jinde v šabloně nebo při validaci, ale do formuláře se skutečně už znovu nedají. Zásadní věc je, že jsi měl pravdu, kdybych to obalil podmínkou, že se to použije jen pokud formulář nebyl odeslán a uvnitř použil setValues dopadlo by to úplně stejně.

Možná tedy naopak já vyzkouším tvůj způsob. Ačkoli 2 akce + společnou továrnu ti stále doporučuji.

Pořád nám ale zůstává na stole popsaná issue s disabled elementy, ty se s formulářem neodesílají takže hodnotu je třeba vždy nastavit znovu. Mimochodem to co jsem popsal disablovanéá prvky neřešilo. Já totiž nevolal setDefauls, ale měl jsemv actionEdit for cyklus který prošel jednotlivé elementy formuláře a nad všema zavolal setDefaultValue, jen nad těmi disabled zavolal setValue. Tím jsem problém disabled prvků sice vyřešil, nicméně to řešení nepovažuji za ideální. Pokud tě napadá něco lepšího, sem s tím. :-)

Nevíš náhodou jak jednoduše otestovat serverovou validaci ale ne tak, abych si musel nainstalovat prohlížeč nepodporující HTML 5 ?

Stačí vypnout javascript. :-P No dobře, některé pokročilé elementy jsou na javascriptu závislé takže tak jednoduše to mnohdy nejde. Formuláře v HTML 5 mohou mít atribut novalidate, který vypne validaci v prohlížeči. Někde už se o tom mluvilo, že Nette validace by se taky měla při tomhle atributu vypnout – nejsem si jistý zda to už v Nette je nebo ne, ale mělo by to být celkem easy. Potom už stačí tenhle doplněk.

EDIT: Aha tak v Nette je to jinak. Záleží zda odesílací tlačítko má atribut formnovalidate.

Editoval enumag (13. 12. 2012 9:40)

Filip Procházka
Moderator | 4668
+
0
-

Musím se též přiznat, že setDefaults jsem už pěkně dlouho nepoužil. Vždy píšu něco jako

public function actionEdit($id)
{
	// základní entitu/řádek stejně potřebuji vždy
	// buď z ní plním formulář, nebo samotnou entitu updatuji
	if (!$this->row = $this->articles->get($id)) {
		$this->error('Article not found');
	}

	if (!$this['articleForm']->submitted) {
		// načítání dalších výchozích hodnot, relací, ..
		// ..
		$this['articleForm']->setValues(..);
	}
}