20× renderování stejného formu/komponenty, např. vložení produktu do košíku
- mcmatak
- Člen | 504
1. Jak řešit více stejných formulářů na jedné stránce!
Opět problém, který se tady omýlá snad 100×, ale konečné rozumné řešení ještě nikdo nenapsal (trochu mne štve, že udělat takovou samozřejmou věc je v nette problém, co takhle vymyslet nějaké systémové řešení tohoto problému)
2. Problémy
- Jak efektivně renderovat a mít co nejméně práce, idálně: {control addToBasket $idproduct}
- v případě XY krát vykreslení stejného formu se budou duplikovat id jednotlivých inputů, dále se bude duplikovat validační javascript validateAddToBasket() bude na stránce 20×, stejně tak jako <input id=„frm-addToBasket-quantity“ name=„quantity“ />
obojí je problém z hlediska validity stránek, duplikování kódu a také případného navěšení javascriptu na jednotlivé idčka
3. Pár vláken, které se toho týkají
jaksi dle mého nefunguje, FormControl::$idMask = ‚frm-‘ . $this->getUniqueId() . ‚-%s-%s‘;, protože getUniqueId() vrací vždy prázdný řetězec a ještě je final
i kdyby to šlo vyřešit, nebude nepředvídatelné jaká ta id vlastně budou? jak pak naprogramovat javascript, aby s nimi pracoval? co když! říkám co když! uvnitř komponenty z nějakého důvodu potřebuji vytvořit nějaký inputu úplně jinému formuláři, nastavení statické metody jaksi vloží uniqueId úplně z jiného formuláře?
mám za to, že opět neuspokojivý výsledek
další se mi nechce hledat, ale obvykle také k ničemu nedospěli
4. Řešení?
Řeším to takhle
foreach($products as $idproduct=>$product) {
{control addToBasket $idproduct}
}
<?php
class addToBasket extends Control {
public function createComponent($name) {
if (preg_match("/^addToBasket/", $name)) {
$form=new AppForm($this, $name);
....
public function render($id) {
$this->template->addToBasket=$this->getComponent('addToBasket'.$id);
$this->template->render();
}
?>
Ale tohle řešení se mi vůbec nelíbí, co s tím? A jak udělat takovouto stránku? http://www.alu-vyprodej.cz/ myslím ty 4 boxíky s produkty, kde se vyplňuje množství
Editoval mcmatak (5. 11. 2009 14:18)
- jasir
- Člen | 746
mcmatak napsal(a):
jaksi dle mého nefunguje, FormControl::$idMask = ‚frm-‘ . $this->getUniqueId() . ‚-%s-%s‘;, protože getUniqueId() vrací vždy prázdný řetězec a ještě je final
Aby nedošlo k nějaké fámě, getUniqueId()
vrací prázdný
řetězec jen v presenteru, implementace v PresenterComponent je
samozřejmě jiná:
<?php
/**
* Returns a fully-qualified name that uniquely identifies the component
* within the presenter hierarchy.
* @return string
*/
public function getUniqueId()
{
return $this->lookupPath('Nette\Application\Presenter', TRUE);
}
?>
Čili se dá přepsat ve vašich potomcích PresenterComponent (tedy
i Controlu).
Nešlo by tedy něco takového?
Edit: Nešlo, kravina, sorry ;-)
<?php
class BaseControl extends Control {
private $id;
public function getUniqueId() {
static $ids = array();
if ($this->id === NULL) {
$id = parent::getUniqueId();
if (isset($ids[$id]) {
$ids[$id] = $ids[$id]+1;
$this->id = $id . "_" . $ids[$id];
} else {
$ids[$id] = 0;
$this->id = $id;
}
}
return $this->id;
}
?>
Je to jenom nápad…
Editoval jasir (5. 11. 2009 18:50)
- mcmatak
- Člen | 504
sory, vím, uvědomil jsem si to pozdě, ale už jsem psal na té stránce s tím hackem jak mi to funguje
tenhle counter mne taky napadl, ale nebude ještě více nepředvídatelné jaké bude id? jak budeš programovat javascript, který např. automaticky sčítá dva inputy při každé změně čísla v jednom z nich
takže ještě jednou pro upřesnění, hack opravdu funguje, viz. zmíněný odkaz někde dole to tam bude vysvětlené
Editoval mcmatak (5. 11. 2009 18:53)
- David Grudl
- Nette Core | 8218
Umístění stejné komponenty vícekrát na jedné stránce není frameworkem podporované. Neříkám, že se by implementace neměla smysl, ale prostě to tak nebylo navrženo a proto toho nelze snadno docílit.
Jak tedy podobný úkol řešit?
- buď vytvořit jeden velký formulář s mnoha containery namísto mnoha malých replikovaných formulářů
- při každém požadavku na widget
{control addToBasket $idproduct}
vytvářet nový formulář s unikátním jménem (např. form-$idproduktu), lze k tomu účelu přepsat metoduControl::getWidget()
- rovnou si v presenteru vytvořit metodu
getBasket($idprodct)
která formulář vrátí a v šabloně ji volat přes{$presenter->getBasket($idProd)}
– tento způsob mi připadá nejvhodnější, protože v okamžiku, kdy by se do Nette dodělala podpora vícenásobného vykreslování, stejně by musela nějaká taková metoda existovat, která by již existující formulář upravila pro další vykreslení, tj. nastavila prvkům nové HTML ID a hodnoty HiddenFieldu.
- mcmatak
- Člen | 504
- nedokážu si to představit, jako že bude formulář začínat pod tagem <body> a končit nad </body>? a pak to zpracování už si vůbec nedokážu představit a validace atd. hmm nno nevím to snad ani ne buď nerozumím nebo nechápu
- no fajn, dejme tomu ze to nejakym zpusobem obchazim, ale! v tomhle neni problem! spousty lidí píše komponenty tak, že formulář uvnitř se jmenuje form, je velice snadné i tady v tomto vytvořit duplicity! další problém je pokud se někdy v budoucnu třeba v potomkovi k formuláři někdo vrací a vytváří nějaké pole, vzhledem k tomu že FormControl::$idMask je statická proměnná! tak se změní i název dalších ideček všech ostatních formulářů tedy nelze
- více zde tady sice řešíš instantclientscript ale když si přečteš pár příspěvků je tam i příklad s tím proč nebude fungovat název idečka při statické proměnné, jde o to, že ne všechny prvky formuláře budeš vytvářet společně, ale třeba v potomkovi apod., v adapteru atd., řešení mi fakt přijde jedině to s tím FormControl::$idMask
- _Martin_
- Generous Backer | 679
mcmatak napsal(a):
1. nedokážu si to představit, jako že bude formulář začínat pod tagem <body> a končit nad </body>? a pak to zpracování už si vůbec nedokážu představit a validace atd. hmm nno nevím to snad ani ne buď nerozumím nebo nechápu
Ikdyby ten formulář byl aplikován na celou stránku (ovšem myslel jsem, že se řeší konkrétní skupina formulářů a ta je v nějakém úžeji zaměřitleném bloku), tak můžeš kdekoliv vykreslit jednotlivé části. A validace i zpracování si nastavíš vždy tam, kde budeš přidávat ten container, přímo na těch prvcích (čili nejspíš v Presenteru). Kde je problém?
- mcmatak
- Člen | 504
Co třeba hlavní logika nette? A to že je postaveno na komponentech, na modulech, které už nebudou moci být zásuvné, nebo naopak budou všechny muset předpokládat že existuje formulář přes celou stránku a jen si musím ukrást prostor v kontaineru a jsme u toho, že zase budeme řešit duplicity kontaineru. A myslím, že tohle řešení bude přinášet spusty jiných problémů, chápu že díky routování v nette v podstatě nepotřebuji nastavovat action. Ale nevím jestli tohle nezpůsobí víc problémů.
A k té skupině, ono představte si formulář na vložení zboží do košíku, formulář na hodnocení článků, apod. článek nebo produkt máte jak v contentu, tak v levém nebo pravém sidebaru, takže vzhledem k tomu, že se formuláře nesmí křížit tak to nikdy nelze chápat jen jako skupinu! Těch stejnorodých skupin může být mnohem více.
- _Martin_
- Generous Backer | 679
Ale tady se nebavíme o obecném řešení, ale o tvém konkrétním. A to sice, že potřebuješ pro každý produkt vypsat jeden formulář (=asi tlačítko a textové políčko). A dle logiky lze soudit, že produkty jsou vypsané někde u sebe a není mezi nimi žádný jiný formulář. Z toho plyne, že celý seznam produktů může být obalen formulářem a u každého produktu může být jeden kontejner.
Netvrdím, že musí být formulář přes celou stránku. A už vůbec ne,
že se takhle paušálně do jednoho velkého elementu form
budou
ukládat všechny formuláře.
- mcmatak
- Člen | 504
no ale právě píšu, že produkty máš v levém sidebaru, pak máš formulář pro přihlášení do novinek, pak máš produkty v contentu, a pak máš přihlášení na stránky a pak máš produkty v pravém sidebaru
tedy právě to nejde jen tak uzavřít, ty formuláře jsou prostě proloženy, takže tohle opravdu není řešení
- _Martin_
- Generous Backer | 679
Aha, tak to jsem si to špatně přečetl. Asi nemá cenu diskutovat o tom,
jestli takový layout je v pořádku, že? Pak mě tedy napadá využít
ten druhý způsob, co David popisoval.
Tedy jak to chápu já: využít na tvorbu formuláře nikoliv klasickou
továrničku createComponentAddToBasket
, ale upravit metodu
createComponent
a to tak, že pokud začíná na jméno žádané
komponenty (třeba na addToBasket), tak vytvoří formulář, jinak
zavolá rodiče. S tím, že jednotlivé formuláře pojmenuješ podle ID
produktu (třeba addToBasket052).
Metodu pro widgety pak upravíš tak, že když je předán ten
parametr, tak ho spojí se jménem toho widgetu. A nebo to spojení uděláš
ručně v tom volání.
Šlo by tohle řešení nebo jsem něco přehlédl?
Edit: nevýhodou je akorát to, že se budou „zbytečně“ generovat
všechny validace zvlášť pro každý formulář. Ale třeba by šlo i toto
nějak vyřešit.
Edit 2: ach jo, jsem asi přepracovanej. Tohle řešení mě napadlo a až pak jsem si přečetl, že to takhle už řešíš… Nicméně mi to přijde jako čisté řešení.
Edit 3: teď jsem to tvé řešení prostudoval lépe a myslel jsem to trochu jinak – mít továrničku v presenteru a rendrování řešit rovnou voláním té komponenty (a nebylo by třeba mít další třídu). Takže asi trochu čistěji, než ty, ale princip zůstává.
Editoval _Martin_ (24. 11. 2009 13:54)
- mcmatak
- Člen | 504
Layout
- takový layout využívá 90% eshopů
- zákazníkovi je nette víš kde a nezajímá ho, že to nejde udělat protože to nette neumí
Metoda 2
Bohužel nefunguje, více v tom druhém článku, na který už sem dvakrát odkazoval, problém je v tom, že nejde ani tak o název formuláře jako o název jednotlivých ideček, 1. název formuláře bohužel jen stěží napříč systémem udržíš unikátní!a i kdyby, pak je tady problém:
- vytvoříš formulář
- vytvoříš jiný formulář
- z nějakého důvodu se potřebuješ vrátit k prvnímu formuláři a přidat mu addText( třeba v potomkovi )
- vytvoříš úplně jiný formulář a už je problém, statická metoda FormControl::$idMask si pamatuje posledni použití bude si pamatovat masku předchozího formuláře, nějak tak je to popsané v tom druhém článku i s příkaldem
- _Martin_
- Generous Backer | 679
Ad layout: jj, jasné.
Ad metoda: moc ti nerozumím.
Resumé: Sedl jsem si k tomu a takový výpis produktů nakódil. Zvolil
jsem takový vlastní způsob, který je založen na metodě
createComponent
. Vlastnosti:
- produkt může být vypsán kdekoliv na stránce
- lze mezi produkty vkládat jiné formuláře
- lze formulář pro přidání produktu upravit i z potomka (logicky se tak upraví všem produktům)
- není třeba znát dopředu počet produktů na stránce
- nevadí, pokud byl mezitím produkt odebrán/vyprodán,… (lze zkusit na
produktu 0459; pro případné puntíčkáře: než něco napíšete, zkuste po
vykreslení stránky zakomentovat v modelu ten produkt i v metodě
getLeft
)
Možná nevýhoda: každý produkt může být na stránce jen jednou.
Appku (pro verzi PHP 5.3) si můžete stáhnout (minified Nette included). Neříkám, že by to nešlo řešit elegantněji. Pravdou zůstává, že tohle řešení jsem napsal velmi rychle, je jednoduché a řekl bych, že i čisté.
Edit: P.S. V .htaccess
si upravte RewriteBase
.
Editoval _Martin_ (24. 11. 2009 15:58)
- mcmatak
- Člen | 504
zdá se, že to máme stejné ne? krom toho, že vše jsem ještě dal do komponent
- trochu oftopic, ale když by si chtěl mít ten block product společný pro XY komponent což tak bude tak ho dáš na začátek layoutu jo? tím pádem layout dost poroste, nešlo by to nějak oddělit?
- uvažoval si nebo zkoušel si toto? https://forum.nette.org/…alidate-name?…
- _Martin_
- Generous Backer | 679
mcmatak napsal(a):
zdá se, že to máme stejné ne? krom toho, že vše jsem ještě dal do komponent
Jj, to už jsem psal dříve, že to máme podobné. Protože nevím o důvodu, proč to dávat do další komponenty, tak jsem to udělal takto. Jinak se lišíme asi jen v tom, že ty dáváš ID do metody render, kdežto já si rovnou volám žádanou komponentu (=form).
1. trochu oftopic, ale když by si chtěl mít ten block product společný pro XY komponent což tak bude tak ho dáš na začátek layoutu jo? tím pádem layout dost poroste, nešlo by to nějak oddělit?
Jistě, není problém načítat to třeba z externího souboru. Tohle bylo dělané jako ukázka, že to jde.
2. uvažoval si nebo zkoušel si toto? https://forum.nette.org/…alidate-name?…
Co přesně myslíš? V tom tvém příspěvku je napsáno, že to nefunguje a ID jsou přeházená. Nebo jsi myslel sjednocení validace?
btw. ještě jeden offtopic, proč tam máš jednou onclick a jednou onsubmit?
To je zvyk. Protože chci po každém úspěšném odeslání formuláře
přesměrovat, dávám přesměrování do onSubmit
. Potom to
nemusím řešit u jednotlivých tlačítek (když jich je víc) ani
u různých podmínek (block try … catch, apod).
- mcmatak
- Člen | 504
- myslel jsem to, žej sou ID přeházená, akorát na tom tvém příkladu se mi to nedaří napodobit, hele až na to že jsem měl formulář ve zvláštní komponentě, to podle mne máme stejné! tak nevím proč se to chová jinak a jakto, že jak se zdá ti to funguje
- jen jestli si správně rozumíme: jak dáš block do jiného souboru?
- ty redirectuješ i v případě neúspěšné validace?
- _Martin_
- Generous Backer | 679
mcmatak napsal(a):
1. myslel jsem to, žej sou ID přeházená, akorát na tom tvém příkladu se mi to nedaří napodobit, hele až na to že jsem měl formulář ve zvláštní komponentě, to podle mne máme stejné! tak nevím proč se to chová jinak a jakto, že jak se zdá ti to funguje
Kouzlo osobnosti =) Jinak já tam nikde nic nedělal s
FormControl::$idMask
, tak jestli to není třeba tím…?
2. jen jestli si správně rozumíme: jak dáš block do jiného souboru?
Aha, asi nerozumíme. Já myslel, že můžeš obsah toho blocku dát do jiného souboru a ten pak includovat.
3. ty redirectuješ i v případě neúspěšné validace?
Úspěšným odesláním jsem myslel odeslání, při kterém projde
validace. Ostatně metoda onSubmit
je volána pouze v případě,
že projde validace, takže je ta otázka trochu zbytečná.
- mcmatak
- Člen | 504
- já právě to testuji bez jakýchkoli zásahů do nette, které byly potřeba, každopádně i kdyby tady ten problém nebyl, což mi přijde divné, protože sem to testoval dost dlouho ze shora i ze spoda, tak stále platí že formuláře komponenty musí mít unikátní název, což může být pro spousty lidí problém!
- no možná i rozumíme, a tak tedy použiješ klasický include souboru v layout jo? takže v layout máš třeba 10× include na podobné vykreslení (ptám se jen ze zájmu jak stavíš aplikaci)
- jasný (pořád mne zmátlo to že dělíš onclick a onsubmit přitom by mělo stačit pouze jedno)
- _Martin_
- Generous Backer | 679
1. Já pořád nevěděl, proč mluvíš o názvu. A ono to je kvůli generovaným ID. To bych viděl jako požadavek na Davida, aby formulářové prvky ve svých ID zohledňovaly to, v jaké komponentě se nacházejí. Neodbočujeme už ale od tématu?
Edit: ostatně už tohle zohledňuje generování signálu pro action, tak by neměl být problém, aby to zohledňovaly i atributy ID.
2. Abych se přiznal, tak jsem tohle zatím nepotřeboval a dnes to bylo prvně, co jsem zkoušel vymyslet řešení, jak použít „jeden“ formulář vícekrát, takže těžko odpovědět, jak stavím aplikace. V tomhle případě bych to asi nechal jako block v té šabloně default.phtml, protože jsem předpokládal, že se to zboží vypisuje jenom tam. V reálu by to bylo třeba víc pohledů na výpis produktů a pak bych to řešil jinak – třeba extendováním nějaké šablony, která by v sobě tenhle block měla.
Editoval _Martin_ (24. 11. 2009 20:25)
- mcmatak
- Člen | 504
1. možná jsem to blbě napsal, ale o tom právě téma je ne? požadavek na Davida už tady existuje dlouhou dobu a dokonce v tom druhém článku je i řešení a to právě změna FormControl::$idMask, otázka je jestli by to tak mělo být, Davidovi se do toho asi moc nechce a nebo právě nevidí problém v tom a myslí si, že stačí změnit název formuláře
problém to právě je, leda že by nová verze nette to řešila jinak než 0.9.1, napíšu na to příklad a dám ho sem, otázka je jestli třeba vložení zboží do košíku by mělo probíhat v komponentě nebo ne (stále čím dál více se přikláním k tomu řešit to v basepresenteru), ale například komponenta pro vyhledávání, tedy jen input a submit, může se na stránce vyskytnout 3× už by bylo vhodné to řešit komponentou (navíc součástí může být implementace autocomplete)
2. hmm :) mas icq? :)
- _Martin_
- Generous Backer | 679
ad 1.:
Já myslel, že řešíme:
- 1 komponenta (třeba presenter) má v sobě X formulářů, ale všechny jsou stejné
a ne
- více instancí jedné komponenty má v sobě formulář a ten má pro každou instanci stejné ID.
A jestli jsem dobře pochopil tvé řešení, tak jsi dal formuláře do komponenty a na komponenty jsi udělal obecnou továrničku, čímž jsi vyřešil a), ovšem zase jsi dosáhl problému b).
Já udělal rovnou obecnou továrničku na formuláře, čímž jsem se problému b) vyhnul (alespoň do té doby, než by někdo udělal komponentu, která by řešila to samé, co řeší už ten presenter – a to je prakticky vyloučené).
P.S. b) je samozřejmě chyba frameworku.
- mcmatak
- Člen | 504
1. tak ono se to samozřejmě dá řešit v presenteru jako více formulářů, ale vzhledem k tomu že takový formulář někdy potřebuje přijímat určitá data skrz funkci render tak někdy je nutné to obalit do komponenty (data například ovlivňují chování formuláře, nebo jednou chci na tlačítko napsat hledej hráče, pak chci napsat na tlačítko hledej článek apod.)
než budeme řešit, že se to dá nějak obejít, stále mám pocit, že to může být relevantní požadavek na nette, to že teď spousty lidí takový problém neřeší neznamená, že je to v budoucnu naprosto legitimní potřeba
a ted když čtu dál, tak máš pravdu, mohl bych udělat komponentu addToBasket ale používat ji takto
$control[‚addToBasket‘][‚form1‘],
$control[‚addToBasket‘][‚form2‘],
$control[‚addToBasket‘][‚form3‘] atd. to je pravda
nicméně jeslti se nemílím, tka jméno komponenty v originál nette do ideček nevstupuje a tak opět nastane kolize v tomto případě
$control[‚addToBasket‘][‚form1‘],
$control[‚addToBasket‘][‚form2‘],
$control[‚addToBasket‘][‚form3‘]
a
$control[‚newsletter‘][‚form1‘],
$control[‚newsletter‘][‚form2‘],
protože idečka budou u obou komponent shodně pojmenována jako
frm-form1-submit apod.
kazdopadně to s tím měněním formuláře narozdíl od změny názvu komponenty mi přijde lepší…asi bych to měl předělat, stále však zůstává ten problém, že název formuláře bude jen stěží unikátní v systému! a tak jeho celá cesta k presenteru by asi mohla být řešením (tedy frm-komponenta-subkomponenty-xxxsubkomponenty-form1-submit) což je pak horší na spolupráci s javascriptem, protože umístění formu se napříč komponentami může měnit, ale šlo by předat cestu a tím i prefix ideček javascriptu v nějaké globální proměnné
- _Martin_
- Generous Backer | 679
Jak jsem již psal, generování názvů ID by mělo zohleďnovat zanoření.
A troufnu si tvrdit, že nikoliv k presenteru, ale rekurzivně vzhledem
k objektům Nette\ComponentContainer
. Je to z toho důvodu, že
teoreticky lze mít i v neMVC aplikaci formuláře se stejnými jmény a
různými rodiči.
Každopádně, dokud bude tato chyba existovat, tak se teoreticky může kdykoliv stát, že budou existovat dva stejně pojmenované formuláře. To jinak než zásahem do Nette řešit nelze. Moje implemntace ovšem opravené chování nevyžaduje, narozdíl od té tvé.
Předávání názvu ID JavaScriptu by se mohlo dít v šabloně (např.
díky:
{$control['addToBasket'.$product->id]['count']->getHtmlId()}
).
Vzhledem k tomu, že je ID generované, tak by se takovýto postup měl
nejspíš použít vždy.
Jinak většinu toho, co tu píšu, vztahuju na ten nákupní košík, neboť to je problém, který jsi chtěl na začátku řešit. Samozřejmě můžeme řešit i jiné problémy, ale už teď to vlákno hezky bobtná.
- _Martin_
- Generous Backer | 679
V mém řešení se formuláře nejmenují stejně a proto jim ta chyba nevadí. Kdybych je dal do komponent a pojmenoval je stejným jménem, tak by jim ta chyba vadit bude. Leda bych v těch komponentách pojmenovával formuláře podle komponent – pak by se každej jmenoval jinak a opět by jim to nevadilo. O závislosti na zanoření nelze uvažovat, protože moje implementace zanoření nevyužívá.
Nevadilo znamená, že si nepřekáží mezi sebou. Samozřejmě je to neochrání, pokud si někde jinde v aplikaci usmyslím vytvořit formulář addTobasket123.
- gdx
- Člen | 26
ahoj mcmatak, viem ze to tvoj problem s formularmi nevyriesi, ale sledujem tento thread, pretoze aj mna to caka, chcem prerobit cely eshop na nette,
neuvazoval si o pouziti JS? pripadne AJAX(ziadny refresh stranky, setris traffic a load) na odoslanie tovaru do kosika?, pokial by mal navstevnik vypnuty JS tak by pridavanie do kosika fungovalo len ako URL odkaz, v nahlade kosika uz by bola kombinacia AJAX / Formulare(pri vypnutom JS)
- _Martin_
- Generous Backer | 679
Nešlo by to s těmi ID řešit stejně, jako řeší nový InstantClientScript generování HTML jména formuláře? Čili změnit řádek ve FormControlu:
// místo současného
$this->htmlId = sprintf(self::$idMask, $this->getForm()->getName(), $this->getHtmlName());
// použít následující
$this->htmlId = sprintf(self::$idMask, ($this->getForm() instanceof AppForm ? $this->getForm()->lookupPath('Nette\Application\Presenter', TRUE) : $this->getForm()->getName()), $this->getHtmlName());
- gdx
- Člen | 26
_Martin_ napsal(a):
gdx napsal(a): …
U odkazu je nevýhoda ta, že nemáš jak zadat počet přidávaných položek. Pokud takový požadavek je (a u eshopů to tak bývá), je jedinou možností použití formuláře.
jj viem, lenze to uz je problem zakaznika ze ma vypnutu podporu JS, naviac v liste produktov sa moznost zadania viac poloziek vyuziva u Eshopov tak 50:50, castokrat si zakaznik zvoli pocet kusov uz len v detaile tovaru, vyuzitie formularov v liste produktov vidim len pre uzku skupinu zakaznikov ktory objednavaju urcity tovar a poznaju ho (nepotrebuju zobrazit detail), lenze polozky v eshope sa vacsinou aktualizuju a pribudaju nove,
… toto vsak nikam nevedie, pockam si na nieco co vymyslite, ale pokial by som to riesil ja, a nahodou by som potreboval mat moznost zadavania rozneho poctu produktov uz v liste, tak by som bud upravil cast nette a poznamenal zmenu pre pripadnu aktualizaciu nette, alebo naprogramoval vlastnu triedu pre taketo forms
Editoval gdx (25. 11. 2009 11:08)
- mcmatak
- Člen | 504
1. eshop a formuláře, na těchto diskusích mne štve, že se většinou řešení stočí k tomu: A nejde to udělat jednodušeji? Nebo třeba vůbec tohle nedělat?
uvědomme si jednu věc! framework mne nijak nesmí omezovat! zákazník který si vymyslí sebevětší hovadinu a to třeba vybírat si i variantu zboží ze seznamu produktů, což už je opět podstatně složitější formulář, tak to musí jít a nemůžu mu říct: víte, ale Váš eshop je dělaný v Nette, tak spadněte na zem!
přesuňme tyto diskuse někam do přístupnosti webu, řešením programátorského/architektonického problému není! opakuji není! udělat to jinak! samozřejmě je potřeba o tom uvažovat, ale to co vyhovuje jednomu nemusí druhému a domnívám se, že naším úkolem je vyřešit to programátorsky … přístupnost už je na managerech, a ne programátorech … byť jste všichni 10 osob v jednom
2. ano řešením je změna toho řádku ve FormControl, nevím jestli bychom tuto diskusi neměli přesunout do toho druhého článku, protože tam ta řešení jsou už napsaná, ale problém je v tom, ta vůle to do nette udělat! přepsat to! nechci si vytvářet vlastní větev nette, pokud je to problém řešme to tak už na úrovni stabilní verze Nette
- mcmatak
- Člen | 504
jinak sorry, už sem si uvědomil jak je to s těmi přeházenými idečky, to se vztahovalo k mému řešení že FormControl se upravuje v konstruktoru BaseForm! Což sem navrhoval jako řešení toho, že se nemusí přepisovat nette ! sorry moje chyba, upravovat statickou vlastnost v BaseForm nelze! jediné řešení je zásah do nette, tím chci přehazování ideček (tento problém) vyškrtnout
- mcmatak
- Člen | 504
a ještě jednou naposledy, co teda vidím že je potřeba
- upravit clientscript, aby neduplikoval podmínky, ale aby se podmínky vylidace načítali v externím souboru
- validační pravidla clientskriptu musí mít svůj namespace tedy např. nette = {}, opět může docházet ke kolizím
- validace formuláře, by měla jít využít i bez formuláře, resp. něco jako Validation::isNotEmpty($value, „Email je povinná hodnota a musí být vyplněn!“) by mělo fungovat!
V čem je u toho problém?
Něco takového používám. Z toho by se pak dalo generovat i ten samý souhrn pravidel v javascriptu. Vznikl by soubor s validačními pravidly. A validační skript by jen bylo nette.validateAddToBasketForm = function{ nette.rules.isNotEmpty(document.getelementbyid)} atd. rozumíte ne? už takhle při 50 formulářích docela strának s javascriptem boptná
- gdx
- Člen | 26
AD: mcmatak
mas pravdu, samozrejmost je ze ked si zakaznik povie, tak to treba spravit a
framework by takuto samozrejmu vec urcite nejak obmedzovat nemal
AD: _Martin_
dakujem velmi pekne za ukazkovu Aplikaciu, je to pre mna vzor (ktory som
potreboval na doplnenie) ako mozno spravne strukturovat kod v tomto MVP
- _Martin_
- Generous Backer | 679
mcmatak napsal(a): …
Co takhle udělat velkou čistku tohoto vlákna (nebo vlákno zamknout a udělat jedno nové) a sepsat pouze ten výsledek naší kilometrové diskuze? =) Kdyžtak pořešíme na ICQ.
A možná bys mohl ten příspěvek o validátorech přesunout do toho tématu s validací, tady – mám obavy – se poměrně dobře ztratí.
gdx napsal(a): …
Nechť dobře poslouží=)
- iguana007
- Člen | 970
Souhlasím s rekapitulací a nějakým finálním řešením v novém
vlákně (nebo i klidně v tomto a uzavřít jej).
Tuto diskuzi jsem sledoval od začátku a abych pravdu řekl, během minulého
týdne jsem přestal, protože jsem se v tom už začal ztrácet.
Podobný problém budu také brzy řešit proto bych uvítal nějakou finální
ukázku řešení.
Díky
iGi
- uestla
- Backer | 799
Já to řeším takto:
V action metodě načítám z modelu produkty. Těmi pak iteruji a pro každý produkt připnu do Presenteru formulář, který vytvářím pomocí vlastní metody Presenteru:
public function actionCateg()
{
$products = $this->template->products = Eshop::getProducts();
foreach ($products as $product) {
$this->createAddToBasketForm($product);
}
}
protected function createAddToBasketForm($product)
{
// pomoci parametru konstruktoru pripnu komponentu k Presenteru
$form = new AppForm($this, 'product' . $product->id);
$form->addHidden('id', $product->id);
$form->addText('ammount', 'ks')
->setDefaultValue(1)
->addRule(Form::FILLED, 'Zadejte prosím počet kusů.')
->addRule(Form::INTEGER, 'Počet kusů musí být číslo.');
$form->addSubmit('add', 'Vložit do košíku');
$form->onSubmit[] = callback($this, 'basketFormSubmitted');
}
public function basketFormSubmitted(AppForm $form)
{
// ... proces vlozeni do kosiku (vetsinou do session)
}
V action metodě jsem si rovnou naplnil šablonu produkty, čili jimi můžu opět iterovat:
{foreach $products as $product}
{var formName => 'product' . $product->id} {* nactu si jmeno formulare *}
{* a mohu jej klasickym zpusobem vykreslit *}
{widget $formName}
{/foreach}
- uestla
- Backer | 799
Používám Nette 1.x a tam to funguje – tvoří se totiž na sobě nezávislé formuláře každý s jiným jménem – jako kdybych psal mylyjón metod
createComponentProduct1
, createComponentProduct2
,
…
Výhoda createComponent<name>
metod je, že se volají až
na požádání, kdežtou mnou ukázané řešení formuláře vytváří
pokaždé (ale když vím, že je na tutti v šabloně použiji, nikterak mne
to netrápí).
- mistm
- Člen | 25
uestla napsal(a):
protected function createAddToBasketForm($product) { // pomoci parametru konstruktoru pripnu komponentu k Presenteru $form = new AppForm($this, 'product' . $product->id); $form->addHidden('id', $product->id); $form->addText('ammount', 'ks') ->setDefaultValue(1) ->addRule(Form::FILLED, 'Zadejte prosím počet kusů.') ->addRule(Form::INTEGER, 'Počet kusů musí být číslo.'); $form->addSubmit('add', 'Vložit do košíku'); $form->onSubmit[] = callback($this, 'basketFormSubmitted'); } public function basketFormSubmitted(AppForm $form) { // ... proces vlozeni do kosiku (vetsinou do session) }
Rad bych se zeptal, jak jsi vyzral na to, ze se odeslanim formulare dalsi krok smeruje treba na /?do=product1187-submit – tedy nedostane se do metody basketFormSubmitted()
- bojovyletoun
- Člen | 667
po odeslání formuláře následovalovalo přesměrování … po uložení
dat do session napiš za metodu $this->redirect('this');
je
to ono?
- dzosincuk
- Člen | 3
@_Martin_ myšleno je to, že při použítí tohoto příkladu se zavolá callback s názvem formuláře například do=product1187-submit místo nadefinovaného callbacku basketFormSubmitted. Vygeneruje to formy s těmito action:
/iivos/form/www/?do=product1-submit
/iivos/form/www/?do=product2-submit
/iivos/form/www/?do=product3-submit
/iivos/form/www/?do=product4-submit
Skoční to chybou, protože žádné handle není nastaveno k product1187-submit
Editoval dzosincuk (20. 12. 2011 16:34)
- dzosincuk
- Člen | 3
Přikládám funkční řešení pro více formulářů na jedné stránce. Funguje zatím pouze v dev verzi nette, kde je již funkční Nette\Application\UI\Multiplier viz (https://forum.nette.org/…i-multiplier)
V této verzi stačí:
protected function createComponentSmaz()
{
$presenter = $this;
return new \Nette\Application\UI\Multiplier(function($id, $control) use ($presenter) {
$form = new Nette\Application\UI\Form($control, $id);
$form->addHidden('id', $id);
$form->addSubmit('submit', 'Smazat');
$form->onSuccess[] = callback($presenter, 'processSmaz');
return $form;
});
}
V šabloně pak stačí volat
{control smaz-1} nebo v cyklu {control „smaz-$post->id“}
- Foowie
- Člen | 269
Já to řeším tak, že v basePresenteru/Controlu mám přepsanou metodu createComponent
protected function createComponent($name) {
$component = parent::createComponent($name);
if ($component !== null)
return $component;
$params = explode("_", $name);
$partialName = reset($params);
$params[0] = $name;
$ucname = ucfirst($partialName);
$method = 'createMultiComponent' . $ucname;
if ($ucname !== $partialName && method_exists($this, $method) && $this->getReflection()->getMethod($method)->getName() === $method) {
$component = call_user_func_array(callback($this, $method), $params);
if (!$component instanceof \Nette\ComponentModel\IComponent && !isset($this->components[$name])) {
$class = get_class($this);
throw new \Nette\UnexpectedValueException("Method $class::$method() did not return or create the desired component.");
}
return $component;
}
}
a pak továrnička vypadá nějak takto
public function createMultiComponentRating($name, $id) {
return new \App\Components\Rating\Control($this->service->getRatingService(), $id);
}
V šabloně pak vykreslím normálně pomocí cyklu komponenty které potřebuju …
{foreach $ids as $id}
{control rating_$id}
{/foreach}
PS: koukám že v názvu komponenty nesmí být podtržítko …