Formulář se nevykreslí při předání proměnné

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

Přes továrničku vytvářím formulář pro přidávání komentářů k článku. Potřebuji aby se do skrytého pole vždy uložila hodnota s id článku. Postup jsem udělal následující:

V presenteru:

public function createComponentAddKomentar($id)
{
	$form = new AppForm;
	$form->addText('jmeno','Jméno')
	     ->addRule(AppForm::FILLED, 'Prosím zadejte jméno.');
	$form->addHidden('clanek',$id);
	$form->addSubmit('pridat', 'Přidat');

	return $form;
}

V šabloně:

{widget addKomentar $vtip->id_vtip}

Pokud odstraním to předávání proměné, formulář se vypíše, takto ne. Kde může být chyba? Díky

Editoval plasmo (20. 10. 2010 12:37)

srigi
Nette Blogger | 558
+
0
-

Chyba je v tom argumente metody createComponentAddKomentar. ID tam urcite necakaj. Musis si ho nastavit nejakym setrom (dopisat do komponenty seter metodu).

plasmo
Člen | 66
+
0
-

srigi napsal(a):

Chyba je v tom argumente metody createComponentAddKomentar. ID tam urcite necakaj. Musis si ho nastavit nejakym setrom (dopisat do komponenty seter metodu).

V Nette moc dlouho nedělám. Jak toho docílím? Děkuji

srigi
Nette Blogger | 558
+
0
-

Bohuzial tu premmenu si cez sablonu asi nepredas, viď Vykreslování komponent. Takze si to ID uloz niekam do presentera alebo modelu a napis to cca takto:

public function createComponentAddKomentar($name)
{
        $form = new AppForm($this, $name);
        $form->addText('jmeno','Jméno')
             ->addRule(AppForm::FILLED, 'Prosím zadejte jméno.');
        $form->addHidden('clanek',$this->vtipId);
        $form->addSubmit('pridat', 'Přidat');
        // return $form; neni treba
}

Editoval srigi (20. 10. 2010 13:35)

plasmo
Člen | 66
+
0
-

možná už vím, u pc zkusím a dyžtak se ozvu

Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Pokud je článek na stránce jeden, určitě bude stačit někde v action nebo render metodě zavolat:

$this['addKomentar']->setDefaultValues(array(
	'clanek' => $idClanku,
));

V okamžiku $this['addKomentar'] se zavolá ta „tovární metoda“ createComponentAddKomentar a následné setDefaultValues nastaví hodnotu toho skrytého pole (které už existuje) přímo v okamžiku, kdy máš to idéčko článku k dispozici.

Editoval vojtech.dobes (20. 10. 2010 15:40)

plasmo
Člen | 66
+
0
-

vojtech.dobes napsal(a):

Pokud je článek na stránce jeden, určitě bude stačit někde v action nebo render metodě zavolat:

$this['addKomentar']->setDefaultValues(array(
	'clanek' => $idClanku,
));

V okamžiku $this['addKomentar'] se zavolá ta „tovární metoda“ createComponentAddKomentar a následné setDefaultValues nastaví hodnotu toho skrytého pole (které už existuje) přímo v okamžiku, kdy máš to idéčko článku k dispozici.

Děkuju za nakopnutí :) Tvůj kód mi to nevzalo, ale vyžeil jsem to následovně:

public function actionText($seo){
       $this->detail = array("clanek"=>$seo); //$seo obsahuje hezkou adresu, též jednoznačnou
 }

A do továrničky:

$form->setDefaults($this->detail);
Vojtěch Dobeš
Gold Partner | 1316
+
0
-

Omlouvám se, mělo to být samozřejmě setDefaults (s tím to bude fungovat určitě:).

MaJJ
Člen | 18
+
0
-

Zdravím,

jak tohle vyřešit, pokud je formulářů (těch {control …}) na stránce více, a ke každému bych potřeboval zavolat jiný argument?

Zkoušel jsem to vyřešit podobně jako na začátku plasmo, ale samozřejmě to nejde:

{block content}

{if count($tricka)}
{foreach $tricka as $tricko}
    Název: {$tricko->nazev}<br>
    {control trickoForm $tricko->id}
    <br>
{/foreach}
{/if}
 protected function createComponentTrickoForm($id_t)
 {
     $velikosti = TrickaModel::FetchSizes($id_t);
     $barvy     = TrickaModel::FetchColors($id_t);
     $kod       = TrickaModel::FetchCode($id_t);

     $form = new AppForm();

     $form->addHidden('kod', $kod);

     $form->addText('kusu', 'Kusů:', 20, 5)
         ->addRule(AppForm::INTEGER, 'Počet kusů je povinný!');

     $form->addSelect('velikost', 'Velikost:', $velikosti)
         ->addRule(AppForm::FILLED, 'Velikost je povinná!');

     $form->addSelect('barva', 'Barva:', $barvy)
         ->addRule(AppForm::FILLED, 'Barva je povinná!');

     $form->addSubmit('objednat', 'Objednat');
     $form->onSubmit[] = array($this, 'trickoForm_onSubmit');

     return $form;
 }

 public function trickoForm_onSubmit(Form $form)
 {
     $tricko = $form->getValues();
     KosikModel::Insert($tricko);

     $this->flashMessage('Úspěšně jste vložili tričko do košíku.');
     $this->redirect('objednavka');
}
Aurielle
Člen | 1281
+
0
-

Továrnička se volá s parametrem $name… ;)

Mikulas Dite
Člen | 756
+
0
-

Jenom doplním, že parametry z {control $name $arguments} jdou do render() metody.

MaJJ
Člen | 18
+
0
-

Still clueless … :)

Netuším, jestli jsem správně pochopil Vaška – do createComponentTrickoForm() to $id_t tedy nepatří … No – ale kam teda? :)

Mikuláši – do render() metody … tím myslíš do této? $control->getWidget("cartControl")->render($tricko->id); (https://latte.nette.org/cs/tags#…) … Tu ale v presenteru nemůžu použít, ne? Nebo jsi myslel renderDefault() v presenteru?

Mikulas Dite
Člen | 756
+
0
-

Jasně, myslím tu render metodu té konkrétní komponenty, tedy to cos psal $control->getWidget("cartControl")->render($tricko->id); je to samé jako {control trickoForm $tricko->id}.

Ten argument pro {control $name, $argument} je navíc u formu pro tohle https://api.nette.org/…rer.php.html#133

Máš to zapeklitější o to, že potřebuješ ten formulář kvůli validaci naplnit i podruhé, po submitu (viz jak funguje formulář/kdy se proč tvoří a validace).

Po vytvoření formuláře přes něj iterovat nemůžeš (můžeš, ale bude to pořád stejné), takže si asi napiš nějakou plnohodnoutnou komponentu, která celý ten foreach zabalí. Nebo dej ten foreach už do té továrničky, rozděl to na groupy a hezky to vyrenderuj. Čisté řešení teď nevymyslím.

MaJJ
Člen | 18
+
0
-

Ha, možná jsem našel řešení… https://forum.nette.org/…zeni-smazani

Každopádně díky moc!

Edit: ikdyž vlastně asi ne … safra. :D

Edit2: ještě pár related linků, které by se mohly hodit lidem se stejným problémem:

https://forum.nette.org/…tu-do-kosiku

https://forum.nette.org/…louzi-widget

https://forum.nette.org/…atecomponent

Editoval MaJJ (17. 11. 2010 1:01)

Ani
Člen | 226
+
0
-

Tu komponentu na tričko si vytvoř jako samostatnou třídu dědící od Control. V ní tu funkci render($id_t).

<?php
class Shirt extends Control {

	private $id_t;

	protected function createComponentTrickoForm() {
		$velikosti = TrickaModel::FetchSizes($this->id_t);

		$form = new AppForm();

		//.......

	    	return $form;
	}

	public function trickoForm_onSubmit(Form $form){}

	public function render($id_t){
		$this->id_t = $id_t;
	}
}
?>

Editoval Ani (16. 11. 2010 23:17)

MaJJ
Člen | 18
+
0
-

Mně už to dneska nemyslí … :D Prosímtě, jak to mám pospojovat? Určitě mně něco chybí asi v tom TrickaPresenteru – ale už nejsem schopný vymyslet, co… :)

Někde bude možná volání new Tricka nebo Tricka::createComponentTrickoForm() nebo tak něco? Achjo. :D

class Tricka extends Nette\Application\Control
{

    private $id_t;

    public function createComponentTrickoForm() {

        $velikosti = TrickaModel::FetchSizes($id_t);
	// (...)

        $form = new AppForm($this,$name);
	// (...)
        return $form;

    }

    public function trickoForm_onSubmit(Form $form)
    {
        $tricko = $form->getValues();
        KosikModel::Insert($tricko);

        $this->flashMessage('Úspěšně jste vložili tričko do košíku.');
        $this->redirect('objednavka');
    }

    public function render($id_t)
    {
            $this->id_t = $id_t;
    }
}

class TrickaPresenter extends BasePresenter
{
    // (...)

    public function createComponentTrickoForm()
    {
    }

    public function trickoForm_onSubmit(Form $form)
    {
    }

}
Ani
Člen | 226
+
0
-

V tom trička presenteru si už nevytváříš ten formulář, ale tu tvojí komponentu, takže tam přidej metodu (tu metodu na formulář můžeš z presenteru odstranit).

<?php
public function createComponentTricko(){
	$tricko = new Tricko;
	return $tricko;
}
?>

V šabloně pak v té iteraci zavoláš:

{control tricko $tricko->id}

Jo a aby se ti ten formulář vykreslil, tak doplň do té render metody Tricka to echo.

<?php

    public function render($id_t)
    {
            	$this->id_t = $id_t;
		echo $this['trickoForm'];
    }
?>

Ono pochopit ze záčátku jak fungují komponenty je trochu náročnější. Já jsem taky tápal přesně na tomhle. Dúležitý je vědět, že komponenta je ta třída dědící od Control a ne ta metoda creatComponent, ta ji jen vytvoří.

Editoval Ani (17. 11. 2010 0:33)

MaJJ
Člen | 18
+
0
-

Díky za pomoc, teď už to běží líp, ale ještě jedna věc mi tam hapruje. Každý další formulář má ty samá data jako ten první. Přijde mi, že se ta komponenta prostě vykreslí jen jednou, dosadí se do ní hodnoty, atd, atd, ale při dalších voláních toho {control …} už se ji to nesnaží udělat znovu. Což bych právěže potřeboval :D Na základě toho argumentu v {control …} měnit obsah formuláře.

MaJJ
Člen | 18
+
0
-

Hmm. Tak nový problém. :D To, že se vykresloval pořád jen jeden formulář, jsem nakonec vyřešil takto:

<?php

use Nette\Application\AppForm;

class TrickaPresenter extends BasePresenter
{
    // (...)
    public function getForm($id_t)
    {
        $velikosti = TrickaModel::FetchSizes($id_t);
        // (...)

        $form = new AppForm($this, "trickoForm" . $id_t);
	// (...)
        $form->onSubmit[] = array($this, 'formSubmit');

        return $form;
    }

    public function formSubmit(AppForm $form)
    {
        $tricko = $form->getValues();
        KosikModel::Insert($tricko);
        $this->flashMessage("Úspěšně jste přidali tričko do košíku!");
        $this->redirect('this');
    }

    // (...)

}

a v šabloně volám:

{$presenter->getForm($tricko->id)}

JENŽE. Sice se všecko vykreslí dobře, ale když některý z formulářů odešlu, vyhodí mně to tuhle výjimku:

(samozřejmě číslo u ‚trickoForm‘ se mění podle toho, který formulář odešlu)

Nette\Application\BadSignalException #403
The signal receiver component 'trickoForm1' is not found.

A já se ptám, jak nastavit to onSubmit[], aby mně to z každého formuláře vždycky zavolalo tu metodu formSubmit (a fungovalo to. :D )…

Ono to potřebuje, aby to byla komponenta? Změní se něco, když to udělám jako Form a ne jako AppForm? Zkoušel jsem to (přičemž před return $form; jsem strčil if isSubmitted()), ale zadrhl jsem se na tom, že jsem nemohl udělat $this->redirect('this'), protože už se stihlo něco vypsat…

Aurielle
Člen | 1281
+
0
-

Kam zmizla ta komponenta? :D
Nemůžeš použít getForm, protože se nezaregistrují signály a formulář se nepřipojí do stromu komponent (což je přesně ta chyba). Nejlepší je použít továrničku, a pokud budeš potřebovat ten parametr tak továrnička+komponenta…

MaJJ
Člen | 18
+
0
-

Aha, tak to jsem si vážně moc nepomoh :) Dobře, rollback zpátky na verzi od Ani. Jak mám ale donutit ten formulář (tedy – jestli to správně chápu – tu komponentu), aby se překreslila nanovo s jinými daty?

Editoval MaJJ (17. 11. 2010 18:23)

MaJJ
Člen | 18
+
0
-

OK, konečně vyřešeno – díky tady tomuhle postu …

Vypadá to nyní (osekáno o zbytečnosti) nějak takhle:

Presenter

<?php

use Nette\Application\AppForm;

class TrickaPresenter extends BasePresenter
{

    // (...)

    public function createComponent($name)
    {
        if(preg_match("#^trickoForm(\d+)$#", $name, $matches)) {
            $id_t = $matches[1];
            $this->createInstanceTrickoForm($name, $id_t);
        }
        parent::createComponent($name);
    }

    protected function createInstanceTrickoForm($name, $id_t)
    {

        $velikosti = TrickaModel::FetchSizes($id_t);
        // (...)

        $form = new AppForm($this, $name);

        $form->addHidden('kod', $kod);
        // (...)
        $form->addSubmit('objednat', 'Objednat');

        $that = $this;

        $form['objednat']->onClick[] = function(Nette\Forms\ISubmitterControl $button) use ($that, $id_t)
        {
            $tricko = $button->getForm()->getValues();
            //KosikModel::Insert($tricko);
            $that->flashMessage('Úspěšně jste vložili tričko do košíku.');
            $that->flashMessage('Kód: ' . $tricko['kod']);
        };

        $form->onSubmit[] = function(AppForm $form) use ($that) {
            $that->redirect('this');
        };

        return $form;
    }

}

View

{foreach $tricka as $tricko}
    Název: {$tricko->nazev}<br>
    <!-- ... -->
    {control 'trickoForm'.$tricko->id}<br>
{/foreach}

Některé věci jsou pro mě pořád španělskou vesnicí, ale na to časem přijdu :) Hodně jsem se při řešení tohoto problému naučil :D Takže díky všem.