Více formulářů na jedné stránce
- joe
- Člen | 313
Ahoj,
setkávám se s dalším „problémem“, na který jsem nenašel na fóru
žádné témata. Rovnou k věci. Z databáze si (přes dibi) vytáhnu
nějakou hromadu řádků a jako pole (teď tuším DibiRow[]) uložím do
proměnné do templatu. Potřebuji mít pro každý ten řádek nový
formulář, jenže nevím jak na to? Mám pole napřed projít před uložením
do template a vytvořit formuláře pro jednotlivé řádky (..new Form())?
Případně jakým způsobem byste to řešili?
Pak ještě předběhnu, dočetl jsem se, že pokud je na stránce více
formulářů na jedné stránce, odesílají se všechny – skutečně se
ještě odesílají všechny – není to zbytečné?
Děkuju za odpovědi
- phx
- Člen | 651
Zdravim.
To ze se odesilaji vsechny je dle meho kachna.
Proc potrebujes co radek to formular??? Nestatilo by tam dat jen vice odesilacich tlacitek a pak si v app detekovat jakym tlacitkem ze to byl formular odeslan? I kdyz pravda je ta, ze v tu chvili se odesle jeden veliky form.
- phx
- Člen | 651
Okoukni to v nejakem prikladu. Pouziti AppForm. (Form je v pouziti bez Nette)
V tom pripade ti nezbyde nic jineho, nez porjit pole vyrobku a pro kazdy vytvorit AppForm a ty stradat do nejakeho pole, ktere predas do sablony. Docela by me jak to nette schroupne. Osobne jsem to netestoval.
- Ondrej
- Člen | 110
phx napsal(a):
V tom pripade ti nezbyde nic jineho, nez porjit pole vyrobku a pro kazdy vytvorit AppForm a ty stradat do nejakeho pole, ktere predas do sablony. Docela by me jak to nette schroupne. Osobne jsem to netestoval.
AppForm bych na to nepouzil. Muze nastat situace, ze po odeslani(pridani do kosiku) nebude jiz vyrobek na aktualni strance, pokud se mezitim prida do databaze vyrobek, ktery ovlivni vypis. V tom pripade by doslo k vyjimce signal pro neexistujici komponentu
Pro tento priklad bych zkusil pouzit normalni Form a jako action nastavit signal pro presenter nebo signal pro komponentu kosik
- joe
- Člen | 313
Ondrej napsal(a):
Muze nastat situace, ze po odeslani(pridani do kosiku) nebude jiz vyrobek na aktualni strance, pokud se mezitim prida do databaze vyrobek, ktery ovlivni vypis. V tom pripade by doslo k vyjimce signal pro neexistujici komponentu
Tak abych pravdu napsal, tohle jsem moc nepochopil, nevím proč by výrobek neměl být na aktuální stránce. Nicméně jsem to udělal pomocí AppForm, funguje mi to a to je v tuhle chvíli hlavní (věřte, že nedělám žádnou důležitou aplikaci, to je jen do školy… a tak jsem se chtěl trošku naučit s Nette :-)).
V čem vidím ale problém je ten výpis formulářů, s tim se asi neskamaradím. Raději bych vypisoval formuláře normálně „ručně“ v šabloně v cyklu u výpisu prodůktů – například (i když tady jsem to udělal už kódem), ale například u výpisu košíku bych potřeboval vypisovat formulář jiným způsobem – tj. na jednu řádku input, vedle něj další případné odkazy… věřím, že to jde nějak přepsat, aby mi to vypisovalo tak jak chci, ale myslím si, že lepší by bylo, kdybych to měl rovnou v šabloně. Je pak nějaký způsob, pokud bych to tak udělal, jak napojit ten natvrdo napsaný formulář na nějaký handler v presenteru?
- joe
- Člen | 313
Už chápu, ale
- Je to málo pravděpodobné, i když stát se to může.
- Při každém výpisu košíku vybírám z databáze právě ty produkty, které v košíku jsou. Pokud nějaký neodpovídá skutečnému produktu z db, není problém ho z košíku odstranit a ohlásit to uživateli.
… ale to už je mimo téma.
- romansklenar
- Člen | 655
joe napsal(a):
Už chápu, ale
1. Je to málo pravděpodobné, i když stát se to může.
Počítat bys s tím měl (popíšu níže).
2. Při každém výpisu košíku vybírám z databáze právě ty produkty, které v košíku jsou. Pokud nějaký neodpovídá skutečnému produktu z db, není problém ho z košíku odstranit a ohlásit to uživateli.
Při každém výpisu košíku vybírej z db jen ty produkty, které jsou validní (je možné je objednat). Možná jsi to tak myslel jen ses překlep. Když má někdo košík nebo výpisu zboží v kategorii 15 minut, už se může stát, že se změní stav (dosupnost) zboží (ať už ho koupí nějaký jiný zákazník, nebo ho admin umaže), takže když zboží do košíku přidá a odešle tak formulář, je třeba tyhle věci kontrolovat.
Pokud bych to měl udělat nějak univerzálně komponentu výpisu zboží v kategorii, tak mě napadá takovédle řešení:
- Vykreslitelná komponenta (Control) – Zboží: bude reprezentovat zboží (výrobek) – máš tím vyřešen ten formulář (input) s polem pro počet kusů (ještě bych zvážil signály místo formulářů, když jsme v komponentě – dalo by se to hezky a jednoduše zajaxovaťet). Komponenta bude mít tu svou logiku (lze ji ještě objednat? atd co je třeba) a zároveň si můžeš udělat více vykreslení (všechny jak budeš potřebovat, včetně manuálního vykreslení formuláře v šabloně) například pro zobrazení ve 3 sloupcích, jen řádky, malé náhledy/velké náhledy, zobrazení v košíku, detail výrobku
- Vykreslitelná komponenta ala DataGrid – Kategorie: můžeš si pro jednoduchost představit, že vypisuje zboží – co řádek to jeden Control Zboží – a zaobaluje je těma obvyklýma věcma okolo (řazení dle ceny, filrt výrobců, zobrazení náhledů, atd). Komponenta může v sobě vykreslit další komponentu, takže není problém. Výhodu vidím v tom, že správnou definicí rout si můžeš tyhle uživatelovy požadavky (řazení, filtry) předávat jako parametry presentru této komponentě a ta už se o všechno hezky postará. Komponenty zboží bych pojmenovával podle jejich tvaru v URL adrese.
- Vykreslitelná komponent – Košík: opět stejný princip jako přehled kategorie akorát s jinou funkčností okolo výpisu.
Teďkom si už jen nadefinovat routy, napsat pro ně pohledy, které budou dělat to co chceš (detail výrobku / výpis) kde budeš registrovat komponenty a je to :) jak krásně a jednoduše to zní :) až mě mrzí, že v Nette nemám momentálně žádnou práci a musím dělat v takovém něčem (nechci říkat v čem), kde jsou tyhle věci otázkou ne hodin ale světelných let. :(
EDIT: Jak to celé může třeba vypadat ve výsledku.
Editoval romansklenar (9. 1. 2009 23:14)
- joe
- Člen | 313
Díky, tak tahle odpověď mi moc pomohla. Jak jsem psal, s Nette se začínám učit a taky nedělám žádnou důležitou aplikaci, na které když se něco udělá špatně, tak to vadí. I teď jsem si vědom, že tam mám plno věcí špatným způsobem, ale jde mi o to, to udělat funkční.
Dal jsem na radu a vytvořil si tedy komponentu pro jeden produkt. Takže můj postup je následující, z db vyberu nějaký result set, který projdu cyklem a pro každý řádek DibiRow vytvořím komponentu a tu uložím do pole, které předám šabloně.
Problém mám ale v tom, že jsem si do té komponenty dal i to vytváření formuláře a mám tam i metodu, která by měla přijmout požadavek po odeslání formuláře, až sem to funguje. V té metodě mám pak přesměrování a to je můj problém. Používám řádku
$this->redirect('Nazevpresenteru:view', array('parametr' => 'hodnota'));
Jenže právě na tomto řádku mi to ztroskotá, píše chybu
Unknown signal ‚Nazevkomponenty:Nazevpresenteru:view!‘.
Vůbec nevím jak to opravit, aby to fungovalo, bez toho se dál nehnu. Kdyby mi někdo mohl pomoc, byl bych rád, děkuju.
- David Grudl
- Nette Core | 8218
Asi by to chtělo
$this->presenter->redirect('Nazevpresenteru:view', array('parametr' => 'hodnota'));
- phx
- Člen | 651
Ondrej napsal(a):
AppForm bych na to nepouzil. Muze nastat situace, ze po odeslani(pridani do kosiku) nebude jiz vyrobek na aktualni strance, pokud se mezitim prida do databaze vyrobek, ktery ovlivni vypis. V tom pripade by doslo k vyjimce signal pro neexistujici komponentu
Timto si nejsem uplne jist. Na strance bude pokazce 20 formularu, ktere se budou lisit akorat nejakou hodnootu v hiddne polozce. Vsechny formy bude obsluhovat jedna a ta sama metoda. Jediny problem by mohl nastat pokud David kontroluje hodnotxy v hidden polozkach zda je nekdo nezmenil. Coz na jednu stranu by mohly, ale nadruhou stranu hidden polozky vyuziva treba JS na odesilani nejakych hodnot.
- Ondrej
- Člen | 110
phx napsal(a):
Timto si nejsem uplne jist. Na strance bude pokazce 20 formularu, ktere se budou lisit akorat nejakou hodnootu v hiddne polozce. Vsechny formy bude obsluhovat jedna a ta sama metoda. Jediny problem by mohl nastat pokud David kontroluje hodnotxy v hidden polozkach zda je nekdo nezmenil. Coz na jednu stranu by mohly, ale nadruhou stranu hidden polozky vyuziva treba JS na odesilani nejakych hodnot.
Nevim, jestli pises o variante 20× AppForm, ale formulare musis nejak unikatne pojmenovat, takze je nejspis budes pojmenovavat form-id_vyrobku. A po odeslani zase vytvoris 20× AppForm ale nezarucis, ze budou mit stejny nazev form-id_vyrobku jako minule. Sekvencni pojmenovani form-position by mohlo fungovat jen v pripade, ze na strance bude vzdy 20 formularu a ne jen 19, pokud se jeden proda.
- joe
- Člen | 313
David Grudl
Děkuju. To je pak jednoduché, když vím co kde je a jak funguje :) Skoro jsem
k tomu už došel.
Ondrej napsal(a):
… ale formulare musis nejak unikatne pojmenovat, takze je nejspis budes pojmenovavat form-id_vyrobku. A po odeslani zase vytvoris 20× AppForm ale nezarucis, ze budou mit stejny nazev form-id_vyrobku jako minule.
Přesně tak formuláře pojmenovávám, i když teď už to dělám v komponentě pro zboží. Jenom nechápu čemu by mělo vadit, pokud bych je pojmenoval stejně jako na předcházející stránce? A nevytvořil bych jich vždycky 20, ale pokaždé ten počet, kolik mám zrovna získaných produktů snad, ne?
- tom
- Člen | 171
joe napsal(a):
…
Dal jsem na radu a vytvořil si tedy komponentu pro jeden produkt. Takže můj postup je následující, z db vyberu nějaký result set, který projdu cyklem a pro každý řádek DibiRow vytvořím komponentu a tu uložím do pole, které předám šabloně.
Problém mám ale v tom, že jsem si do té komponenty dal i to vytváření formuláře a mám tam i metodu, která by měla přijmout požadavek po odeslání formuláře, až sem to funguje.
…
Muzu se zeptat jakym zpusobem pak ten formular zpracovavas? Delam to same a nedari se mi ziskat ty hodnoty z formulare … snazim se o to pomoci handleru. Nevim ale zda je to to prave.
- joe
- Člen | 313
tom napsal(a):
Muzu se zeptat jakym zpusobem pak ten formular zpracovavas? Delam to same a nedari se mi ziskat ty hodnoty z formulare … snazim se o to pomoci handleru. Nevim ale zda je to to prave.
Napíšu jak to přesně mam, třeba to k něčemu bude. Nevím jestli to tak je správně, ale fungovalo mi to.
Takže, mam nějakou komponentu, v ní mam veřejný parametr $form, do kterého přidávám nový AppForm. Přidávám do něj různý inputy, hodnoty… apod. a na konci mam
$this->form->onSubmit[] = array($this, 'addToCartSubmitted');
Ten formulář ($form) si pak předávám někde do šablony. To znamená, že po odeslání toho formuláře se zavolá metoda addToCartSubmitted v ty samý třídě (komponentě).
Na tu metodu pak mam kód
public function addToCartSubmitted(AppForm $form) { ... }
Kde jako jeden z posledních řádků je přesměrování na nějaký presenter.
No, nevím jestli ti to pomohlo :-)
- tom
- Člen | 171
Ten formulář ($form) si pak předávám někde do šablony. To znamená, že po odeslání toho formuláře se zavolá metoda addToCartSubmitted v ty samý třídě (komponentě).
Já se snažím hodit komponentu (Produkt), která má formulář s inputem a odesílacím tlačítkem do řádku komponenty Grid, která vypisuje všechny produkty. A na každém řádku bych právě potřeboval mít tu jednu komponentu s tím formem. Což mi zatím nějak nejde :(
- tom
- Člen | 171
Mám control ProduktVKosiku, ktery ma v sobe formular slouzici k manipulaci s danym produktem.
<?php
class ProduktVKosiku extends Control
{
/** @var int */
protected $pid; // produkt id
protected $iterator;
public $form;
public function bindData($id, $iterator)
{
$this->pid = $id;
$this->iterator = $iterator;
$this->form = new AppForm($this, 'form'.$this->iterator);
$this->form->addText('pocet', 'Počet:');
$this->form->addSubmit('submit'.$this->iterator, 'Odeslat');
$this->form->onSubmit[] = array($this, 'productSubmitted');
}
public function productSubmitted(AppForm $form)
{
...
}
public function renderProduktVKosiku()
{
$this->form->render();
}
}
?>
Tento control umistuju do dalsiho controlu DataGridKosik, ktery vykresluje datovy grid, kde na kazdem radku chci mit onen control ProduktVKosiku
<?php
class DataGridKosik extends Control
{
...
protected $produkt;
...
public function bindData()
{
...
$this->produkt = new ProduktVKosiku($this, 'pvk');
}
public function renderGrid()
{
//vykresli datagrid
...
$template->produkt = $this->produkt;
...
$template->setFile(dirname(__FILE__) . '/datagridkosik.phtml');
...
}
}
?>
a k tomu datagridkosik.phtml
<table class="grid" border="1">
<tr>
{foreach $columns as $column}
<th><a href="{link order $column}"
{if isset($order[$column])} class="{$order[$column][0] === 'a' ? 'asc' : 'desc'}"{/if}
>{$column}{if count($order) > 1 && isset($order[$column])} <span>{$order[$column][1]}</span>{/if}</a></th>
{/foreach}
<th> </th>
</tr>
{foreach $rows as $row}
<tr{!$iterator->isEven() ? ' class="alt"' : ''}>
<td>{$row['id']}</td>
<td>{$row['Název']}</td>
<td>{$row['Počet']}</td>
<td>{$row['Cena']|currency}</td>
<td>{?$produkt->bindData($row['id'], $iterator->getCounter());$produkt->renderProduktVKosiku()}</td>
</tr>
{/foreach}
</table>
Nedaří se mi ale, pri kliknuti na tlacitku v libovolnem formulari (na
radcich toho gridu) dochazi k chybe InvalidArgumentException
Component with name ‚form1‘ does not exist.. Číslo formuláře
záleží na čísle řádku, na kterém ten formulář je (form1, form2,
form3, …)
Tusim v cem je asi chyba, akorat nevim jak ji odstranit, poradil by mi nekdo prosim? Diky. Nebo pokud ma nekdo lepsi reseni … Diky moc
- vlki
- Člen | 218
Pravděpodobně nemáš ty formuláře a komponenty zaregistrované před
fází zpracování signálů. Toto se dá řešit tak, že všechny registrace
komponent uvedeš vždy v metodě createComponent
. Lazy loading ti
pak zajistí to, že pokud bude komponenta při zpracování signálu potřeba,
inicializuje se. Viz https://forum.nette.org/…iewtopic.php?…
- tom
- Člen | 171
vlki napsal(a):
Pravděpodobně nemáš ty formuláře a komponenty zaregistrované před fází zpracování signálů. Toto se dá řešit tak, že všechny registrace komponent uvedeš vždy v metodě
createComponent
. Lazy loading ti pak zajistí to, že pokud bude komponenta při zpracování signálu potřeba, inicializuje se. Viz https://forum.nette.org/…iewtopic.php?…
a kde ty formulare zaregistrovat a jak?
komponenta ProduktVKosiku obsahujici formular je vlozena do komponenty DataGridKosik (do kazdeho radku gridu jedna komponenta ProduktVKosiku s tim formem) a tato je v presenteru KosikPresenter … hrozne by mi pomohla nejaka ukazka. diky moc
- vlki
- Člen | 218
Oukej, zkusim ti to tady rozepsat. V presenteru KosikPresenter
si vytvoris, podobne jako v „posvätnem akrabate“, metodu
createComponent
, ktera bude registrovat tu komponentu
DataGridKosik
. Asi takto…
<?php
presenter KosikPresenter extends Presenter
{
...
protected function createComponentKosik($name)
{
return new DataGridKosik();
}
...
}
?>
V komponente DataGridKosik
by pak, podle nazvu melo byt tolik
komponent ProduktVKosiku
, kolik je produktu, ne? Takhle to vypada,
ze je tam jen jedna komponenta ProduktVKosiku
a pod ni vsechny
formy…? Zkusim to nastinit tak, aby ProduktVKosiku
byl tolikrat,
kolik je druhu produktu…
<?php
class DataGridKosik extends Control
{
...
public function __construct($parent = NULL, $name = NULL)
{
// tady se lazy loading nehodi, stejne musime vsechny produkty vypsat
$products = Products::get(); // pseudokod - ziskani produktu pro vypsani
foreach($products as $p) {
$produkt = new ProduktVKosiku($this, 'produkt' . $p['id']);
$produkt->pid = $p['id'];
}
}
public function renderGrid()
{
//vykresli datagrid
...
$template->setFile(dirname(__FILE__) . '/datagridkosik.phtml');
...
}
}
?>
A nakonec samotny ProduktVKosiku
…
<?php
class ProduktVKosiku extends Control
{
/** @var int */
public $pid; // produkt id
protected function createComponentForm($name)
{
// tady se lazy loading hodi... treba nekdy nebude potreba zrovna vypsat do radku formular,
$form = new AppForm();
$form->addText('pocet', 'Počet:');
$form->addSubmit('odeslat', 'Odeslat');
$form->onSubmit[] = array($this, 'productSubmitted');
return $form;
}
public function productSubmitted(AppForm $form)
{
$produkt = $form->getParent();
$produkt->pid; // takhle bys mel ziskat pid pri zpracovani formulare
...
}
public function renderProduktVKosiku()
{
$this->getComponent('form')->render();
}
}
?>
A nakonec sablona…
<table class="grid" border="1">
<tr>
{foreach $columns as $column}
<th><a href="{link order $column}"
{if isset($order[$column])} class="{$order[$column][0] === 'a' ? 'asc' : 'desc'}"{/if}
>{$column}{if count($order) > 1 && isset($order[$column])} <span>{$order[$column][1]}</span>{/if}</a></th>
{/foreach}
<th> </th>
</tr>
{foreach $rows as $row}
<tr{!$iterator->isEven() ? ' class="alt"' : ''}>
<td>{$row['id']}</td>
<td>{$row['Název']}</td>
<td>{$row['Počet']}</td>
<td>{$row['Cena']|currency}</td>
<td>{$component->getComponent('produkt' . $row['id'])->renderProduktVKosiku()}</td>
</tr>
{/foreach}
</table>
Tohle by ti melo zajistit hladky prubeh. Doufam, ze jsem tou zmenou moc
nenarusil tvou predstavu. Ty jsi totiz predtim mel registrovanou jen jednu
komponentu ProduktVKosiku
, ktere jsi menil promennou
pid
. A presny duvod, proc ti to predtim nejelo? Registroval jsi
formular az pri samotnem renderovani stranky. A to uz je pozde.
Jinak jsem to netestoval, takze je mozne, ze pri copy&paste to nepojede. Ale chtel jsem to hlavne demonstrovat.
Editoval vlki (14. 3. 2009 20:51)
- tom
- Člen | 171
vlki napsal(a):
Tohle by ti melo zajistit hladky prubeh. Doufam, ze jsem tou zmenou moc nenarusil tvou predstavu. Ty jsi totiz predtim mel registrovanou jen jednu komponentu
ProduktVKosiku
, ktere jsi menil promennoupid
. A presny duvod, proc ti to predtim nejelo? Registroval jsi formular az pri samotnem renderovani stranky. A to uz je pozde.Jinak jsem to netestoval, takze je mozne, ze pri copy&paste to nepojede. Ale chtel jsem to hlavne demonstrovat.
Diky moc jsi mi pomohl, uz mi to funguje :)
- Santas
- Člen | 11
Mať teda na jednej stránke viac obyčajných Formov nejde a odchytávať ich klasickým isSubmitted()? (V prípade, že by sme ich neoverovali, že boli poslané tým a tým tlačítkom, keďže užívateľ môže stlačiť Enter)
Edit: nakoniec isSubmittedBy() odchytáva aj Enter :)
Editoval Santas (6. 7. 2009 14:10)
- joe
- Člen | 313
Takže jsem se dostal do stejné situace jako jsem kdysi psal. Pokud jsem
správně pochytil vaše odpovědi, tak mám namísto AppForm
použít Form
, který nekontroluje, zda se tam bude položka
ještě nacházet – pokud by došlo ke smazání produktu.
Další problém je, že pokud pro formuláře vytvořím validační pravidla a těch formulářů v cyklu vypíšu třeba 40 (tj. 40× vypíšu komponentu pro produkt), pak se mi tam i 40× vloží stejný kus kódu v JavaScriptu pro validování. Jde to nějakým způsobem obejít, že bych vypsal jen těch 40 formulářů s jedním klientským skriptem, teď mě napadlo, že validační pravidla přiřadím jen vždy tomu prvnímu formuláři.
Tak teď na to koukám, to by vlastně nešlo. Protože každý form má unikátní název…
Nebo řešíte někdo výpis více formulářů na jedné stránce úplně nějak jinak? Třeba se to za tu dobu trochu změnilo :-)