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 | 8285
 
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
->addConditionOnna 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)