Volání továrničky z beforeRender() : InvalidArgumentException

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

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
+
0
-

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
+
0
-

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?

Jod
Člen | 701
+
0
-

Skôr sa mi zdá, že v nejakom potomkovi zabúdaš volať parent::createComponent($name);

jarks
Člen | 94
+
0
-

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
+
0
-

Návod jsem zaktualizoval, zkus podle něj továrničku upravit a nebude problém.

jarks
Člen | 94
+
0
-

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?

David Grudl
Nette Core | 8228
+
0
-

Potřeboval bych vidět celý kód a výpis výjimky.

jarks
Člen | 94
+
0
-

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
+
0
-

V jedné z posledních revizí Nette byly továrničky udělány case-sensitive a první písmenko názvu je vždy malé. Zkus formulář renderovat pomocí {control form1} a v metodě renderPrehled() vynechat přiřazení formuláře do šablony.

jarks
Člen | 94
+
0
-

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.

Panda
Člen | 569
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

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
+
0
-

jasir napsal(a):
No, to je jasný, musíš volat v ní volat parent::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
+
0
-

jarks napsal(a):

jasir napsal(a):
No, to je jasný, musíš volat v ní volat parent::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)