Volání továrničky z beforeRender() : InvalidArgumentException
- jarks
- Člen | 94
Dobrý den, potřebuji 2 vyhledávací formuláře, které jsou vidět po
celou dobu, v @layout
tedy mám
{$form1}
{$form2}
. Dokud byl jen jenden, vytvářel
jsem ho v BasePresenteru $form = new Form;
. Pak jsem přidal
druhý, ale vždy se odeslaly oba – praly se.
Přečetl jsem si návod, pochopil, že musím použít AppForm a udělal toto:
class BasePresenter extends Presenter {
protected function beforeRender() {
// jestli to má být v @layout, tak to snad musí být tady,
// ostatní presentery obsahují `extends BasePresenter`
$this->template->form1 = $this->getComponent('form1');
}
// továrnička:
protected function createComponent($name) {
switch ($name) {
case 'form1':
$form = new AppForm($this, $name);
$form ->addText('searchText', 'Najít text:');
$form->addSubmit('hledat', '>>');
$form->onSubmit[] = array($this, 'searchFormOnSubmit');
return;
default:
parent::createComponent($name);
return;
}
}
}
A na to obdržím:
InvalidArgumentException, Component with name 'form1' does not exist.
Je jedno, jestli beforeRender()
je public
nebo
protected
. Pokud je public
továrnička, zařve to,
že takové musí být i ostatní, které BasePresenter dědí (…must be
public (as in class BasePresenter).
Kde by mohla být chyba? Dík.
- romansklenar
- Člen | 655
V beforeRender už je na vytváření formuláře nejspíše pozdě. Zkus dát do šablony {control form1} pokud používáš poslední revize, nebo přesuň plnění šablony do dřívějších metod v životním cyklu.
- Ondřej Mirtes
- Člen | 1536
romansklenar napsal(a):
V beforeRender už je na vytváření formuláře nejspíše pozdě. Zkus dát do šablony {control form1} pokud používáš poslední revize, nebo přesuň plnění šablony do dřívějších metod v životním cyklu.
Je to divné, já tam žádnou chybu nevidím. Vždyť s továrničkami je přeci jedno, kde ji plním do šablony, ne?
- jarks
- Člen | 94
Jod napsal(a):
Skôr sa mi zdá, že v nejakom potomkovi zabúdaš volať parent::createComponent($name);
Přesně tak, všechny ostatní presentery, které dědí BasePresenter a
obsahují továrničku musí obsahovat
také parent::createComponent($name);
protected function createComponent($name) {
parent::createComponent($name); // <<<
switch ($name) {
case // .....
To jsem si neuvědomil, díky.
- David Grudl
- Nette Core | 8228
Návod jsem zaktualizoval, zkus podle něj továrničku upravit a nebude problém.
- jarks
- Člen | 94
David Grudl napsal(a):
Návod jsem zaktualizoval, …
Rozdíl je že místo
$this->template->form1 = $this->getComponent('form1');
můžu napsat
$this->template->form1 = $this['form1'];
a místo CASE konstrukce v pořád stejné metodě
createComponent
můžu vytvářet vícero továrniček
podle vzoru
protected function createComponentNázevKomponenty() {}
a tím pádem je nemusím v potomčích presenterech volat, když se jmenuje každá jinak?
Ale nefunguje to. Dostanu
InvalidArgumentException, Component name must be string, NULL given.
Nette 0.9, 425. Neměnilo se něco?
- jarks
- Člen | 94
Raději jsem upgradoval Nette na 0.9, rev. 464. Zkopíroval jsem si doslovně ten příklad.
public function renderPrehled() {
$this->template->form = $this['form1'];
}
protected function createComponentForm1() {
$form = new AppForm;
$form->addText('meno', 'Meno:');
$form->addSubmit('odoslat', 'Odoslať');
return $form;
}
A bez ohledu na to, jestli to zkouším volat jako
$this['form1']
nebo až v šabloně {control Form1}
dostanu teď
InvalidArgumentException, Component with name 'Form1' does not exist.
File: …/libs/Nette/ComponentContainer.php Line: 157
Line 150: $this->createComponent($name);
Line 151: }
Line 152:
Line 153: if (isset($this->components[$name])) {
Line 154: return $a === FALSE ? $this->components[$name] : $this->components[$name]->getComponent($ext, $need);
Line 155:
Line 156: } elseif ($need) {
Line 157: throw new InvalidArgumentException("Component with name '$name' does not exist.");
Line 158:
Line 159: } else {
Line 160: return NULL;
Line 161: }
Line 162: }
Když továrničku přepíšu takto, už to funguje:
protected function createComponent($name) {
parent::createComponent($name);
switch ($name) {
case 'form1':
$form = new AppForm($this, $name);
$form->addText('meno', 'Meno:');
$form->addSubmit('odoslat', 'Odoslať');
return $form;
}
}
Editoval jarks (25. 7. 2009 23:35)
- Panda
- Člen | 569
jarks napsal(a):
Panda napsal(a):
V jedné z posledních revizí Nette byly továrničky udělány case-sensitive…Taky mě to už napadlo. Bohužel ani tak ne. Pořád to hlásí, že
Component with name 'form1' does not exist
.
Já s továrničkami nemám žádný problém, zkus sem postnout celý presenter a celou šablonu, třeba něco najdeme…
- jarks
- Člen | 94
Ještě jedna divnost: Záleží při psaní validačních pravidel na
pořadí prvků? Když použiju ->addConditionOn
na prvek,
který je v kódu až za touto podmínkou, dostanu chybu
InvalidArgumentException, Component with name 'user' does not exist
.
Jde o přehled, který lze generovat za uživatele nebo obdbobí (od, do), nebo obojí. Zkouším napsat pravidlo: „když nebude uživatel vybrán, vyžaduj vyplnění datumů“.
V šabloně se volá {control formPrehled} a toto funguje:
protected function createComponent($name) {
parent::createComponent($name);
switch ($name) {
case 'formPrehled':
$form = new AppForm($this, $name);
$users = array('?' => 'zvolte jméno', 'Aktivní: ' => $usersActive, 'Neaktivní: ' => $usersPassive);
$form->addSelect('user', 'Uživatel:', $users); //<< před podmínkami
$form->addDatePicker('datum_od', 'od ', 10)
->addConditionOn($form['user'], Form::EQUAL, '?')
->addRule(Form::FILLED, 'Zadejte prosím datum.');
$form->addDatePicker('datum_do', 'do ', 10)
->addConditionOn($form['user'], Form::EQUAL, '?')
->addRule(Form::FILLED, 'Zadejte prosím datum.');
$form->addSubmit('zobrazit', 'zobrazit >>');
$form->onSubmit[] = array($this, 'prehledFormOnSubmit');
return $form;
}
}
Ovšem jakmile přehodím select pro uživatele ZA políčka s datumem, stránka se vůbec nevykreslí a vyhodí to výše popsanou chybu. Toto nefunguje:
protected function createComponent($name) {
parent::createComponent($name);
switch ($name) {
case 'formPrehled':
$form = new AppForm($this, $name);
$users = array('?' => 'zvolte jméno', 'Aktivní: ' => $usersActive, 'Neaktivní: ' => $usersPassive);
$form->addDatePicker('datum_od', 'od ', 10)
->addConditionOn($form['user'], Form::EQUAL, '?')
->addRule(Form::FILLED, 'Zadejte prosím datum.');
$form->addDatePicker('datum_do', 'do ', 10)
->addConditionOn($form['user'], Form::EQUAL, '?')
->addRule(Form::FILLED, 'Zadejte prosím datum.');
$form->addSelect('user', 'Uživatel:', $users); //<< tady je změna, za podmínkami
$form->addSubmit('zobrazit', 'zobrazit >>');
$form->onSubmit[] = array($this, 'prehledFormOnSubmit');
return $form;
}
}
Výpis chyby:
File: …/libs/Nette/ComponentContainer.php Line: 157
Line 157: throw new InvalidArgumentException("Component with name '$name' does not exist.");
- Panda
- Člen | 569
jarks napsal(a):
Ještě jedna divnost: Záleží při psaní validačních pravidel na pořadí prvků? Když použiju
->addConditionOn
na prvek, který je v kódu až za touto podmínkou, dostanu chybu
InvalidArgumentException, Component with name 'user' does not exist
.
Záleží. V metodě Form::addConditionOn
se předává
instance prvku. Pokud prvek neexistuje, zcela logicky dojde k výjimce.
Řešení je následující:
<?php
protected function createComponent($name) {
parent::createComponent($name);
switch ($name) {
case 'formPrehled':
$form = new AppForm($this, $name);
$users = array('?' => 'zvolte jméno', 'Aktivní: ' => $usersActive, 'Neaktivní: ' => $usersPassive);
$form->addDatePicker('datum_od', 'od ', 10);
$form->addDatePicker('datum_do', 'do ', 10)
$form->addSelect('user', 'Uživatel:', $users);
// definice podmínek přesunuta sem
$form['datum_od']->addConditionOn($form['user'], Form::EQUAL, '?')
->addRule(Form::FILLED, 'Zadejte prosím datum.');
$form['datum_do']->addConditionOn($form['user'], Form::EQUAL, '?')
->addRule(Form::FILLED, 'Zadejte prosím datum.');
$form->addSubmit('zobrazit', 'zobrazit >>');
$form->onSubmit[] = array($this, 'prehledFormOnSubmit');
return $form;
}
}
?>
Editoval Panda (26. 7. 2009 18:50)
- jarks
- Člen | 94
Panda napsal(a):
Záleží.
Díky, to mě nenapadlo.
Ještě ty továrničky: zkusil jsem to na holém skeletonu a tam fungují bezvadně. Je to něco v aplikaci. Ta v současné době už dost narostla a asi je nedokonale napsaná, je to s Nette moje první. Nevím, co bych sem měl poslat, aby z toho bylo něco vidět. Snad na to časem příjdu.
- kravčo
- Člen | 721
jarks napsal(a):
protected function createComponent($name) { parent::createComponent($name); switch ($name) { case 'formPrehled': $form = new AppForm($this, $name); // ... return $form; } }
Pokiaľ používaš starú továrničku, je lepšie písať:
protected function createComponent($name)
{
switch ($name) {
case 'form1':
$form = new AppForm($this, $name);
// ...
return;
default:
parent::createComponent($name);
return;
}
}
Ale úplne najlepšie bude, ak nájdeš chybu a začneš používať novú továrničku…
- jarks
- Člen | 94
kravco napsal(a):
Ale úplne najlepšie bude, ak nájdeš chybu a začneš používať novú továrničku…
Díky. Tak jsem to konečně našel. Nová továrnička nefunguje v případě, že v rodičovské třídě se nachází továrnička stará. Jakmile do BasePresenteru vložím konstrukci
protected function createComponent($name) {}
klidně i takto prázdnou, vyprodukuje použití nové továrničky jak
v tomtéž, tak v jakémkoliv potomčím presenteru chybu
InvalidArgumentException, Component with name '....' does not exist
.
Vybruslil jsem z toho tak, že staré továrničky v BasePresenteru jsem
přepsal na nové, když jsem přišel na to, že před tím mi tam nefungovaly
proto, že u názvů komponent záleží na velikosti písmen.
{control Form1}
(špatně) není totéž co
{control form1}
(dobře). Motanice.
Když jsou v rodičovském presenteru nové továrničky a v potomčích staré, celkem logicky se nic neděje a všechno funguje.
Otázka je, jestli to není chyba a jestli by informace o case sensitive názvech komponent neměla být někde v tom návodu uvedena.
Editoval jarks (27. 7. 2009 19:36)
- jasir
- Člen | 746
jarks napsal(a):
kravco napsal(a):
Ale úplne najlepšie bude, ak nájdeš chybu a začneš používať novú továrničku…Díky. Tak jsem to konečně našel. Nová továrnička nefunguje v případě, že v rodičovské třídě se nachází továrnička stará. Jakmile do BasePresenteru vložím konstrukci
protected function createComponent($name) {}
No, to je jasný, musíš v ní volat
parent::createComponent($name)
. Pokud to dodržíš, klidně
můžeš typy továrniček kombinovat podle potřeby ;-)
Editoval jasir (27. 7. 2009 21:39)
- jarks
- Člen | 94
jasir napsal(a):
No, to je jasný, musíš volat v ní volatparent::createComponent($name)
.
Hergot, je to tak. Dík.
Mám v tom ještě pořád zmatek. Moc nerozumím, jak to, že předtím
celá aplikace chodila, ačkoliv stará továrnička v BasePresenteru
parent::createComponent($name)
NEobsahovala a zhavarovalo to
teprve, když jsem se pokusil současně použít továrničku novou.
Takže se opravuju: Všechno bylo způsobeno tím, že jsem v rodičovském presenteru z CASE konstrukce omylem vypustil část
//...
default:
parent::createComponent($name);
return;
a myslel si, že je všechno v pořádku, protože to fungovalo.
Editoval jarks (27. 7. 2009 20:28)
- jasir
- Člen | 746
jarks napsal(a):
jasir napsal(a):
No, to je jasný, musíš volat v ní volatparent::createComponent($name)
.Hergot, je to tak. Dík.
Mám v tom ještě pořád zmatek. Moc nerozumím, jak to, že předtím celá aplikace chodila, ačkoliv stará továrnička v BasePresenteru
parent::createComponent($name)
NEobsahovala a zhavarovalo to teprve, když jsem se pokusil současně použít továrničku novou.
Nette, když hledá komponentu pomocí getComponent('name')
a
nenajde ji, volá metodu createComponent($name)
. V původní verzi
jsi tedy dodal celou metodu createComponent
, která zajistila
registrování všech komponent. Nová „supertovárnička“ není nic
jiného, než její chytrá implementace v Presenteru
, která se
pokusí zavolat pro dané jméno $name
metodu
`createComponent<Name>. Tuto metodu jsi zakryl, ale nezavolal parent.
Takže se opravuju: Všechno bylo způsobeno tím, že jsem v rodičovském presenteru z CASE konstrukce omylem vypustil část
a myslel si, že je všechno v pořádku, protože to fungovalo.
Ano.
Editoval jasir (27. 7. 2009 21:56)