20× renderování stejného formu/komponenty, např. vložení produktu do košíku

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

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í

hack s FormControl::$idMask


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

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

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)

mcmatak
Člen | 490
+
0
-

takže ještě jednou pro upřesnění, opravud hack nefunguje!!! zatím v nette netuším jak tohle řešit

David Grudl
Nette Core | 8136
+
0
-

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?

  1. buď vytvořit jeden velký formulář s mnoha containery namísto mnoha malých replikovaných formulářů
  2. 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 metodu Control::getWidget()
  3. 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 | 490
+
0
-
  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
  2. 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
  3. 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
+
0
-

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

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

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

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

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

Layout

  1. takový layout využívá 90% eshopů
  2. 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:

  1. vytvoříš formulář
  2. vytvoříš jiný formulář
  3. 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 )
  4. 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
+
0
-

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

škoda že je to v 5.3

_Martin_
Generous Backer | 679
+
0
-

mcmatak napsal(a):

škoda že je to v 5.3

Pročpak? Když nechceš PHP 5.3, tak si to přepiš. Stačí dát pryč jmenné prostory a nahradit těch pár anonymních funkcí. Jinak tam nic spešl z PHP 5.3 není.

mcmatak
Člen | 490
+
0
-

zdá se, že to máme stejné ne? krom toho, že vše jsem ještě dal do komponent

  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?
  2. uvažoval si nebo zkoušel si toto? https://forum.nette.org/…alidate-name?…
mcmatak
Člen | 490
+
0
-

btw. ještě jeden offtopic, proč tam máš jednou onclick a jednou onsubmit?

stačí snad jen onsubmit ne? nette už dávno přece například situaci kdy odentruješ a tak odešleš form to přiřadí prvnímu submitu ne?

_Martin_
Generous Backer | 679
+
0
-

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 | 490
+
0
-
  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
  2. jen jestli si správně rozumíme: jak dáš block do jiného souboru?
  3. ty redirectuješ i v případě neúspěšné validace?
_Martin_
Generous Backer | 679
+
0
-

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 | 490
+
0
-
  1. 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!
  2. 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)
  3. 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
+
0
-

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

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

ad 1.:

Já myslel, že řešíme:

  1. 1 komponenta (třeba presenter) má v sobě X formulářů, ale všechny jsou stejné

a ne

  1. 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 | 490
+
0
-

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

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á.

mcmatak
Člen | 490
+
0
-

Soustřeďme se na podstatu věci:

říkáš, že tvé řešení není závislé na zanoření a že to funguje i s chybou Nette, to ale přece nefunguje? Vždyť já opravdu používal totožné řešení, zkus si ten form hodit do komponenty (to jsou naše jediné rozdíly).

_Martin_
Generous Backer | 679
+
0
-

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

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

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.

_Martin_
Generous Backer | 679
+
0
-

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

_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)

_Martin_
Generous Backer | 679
+
0
-

Mrkni na tu ukázkovou appku, co jsem dělal. Tak, jak je udělaná, jí ta Nette chyba nevadí.

mcmatak
Člen | 490
+
0
-

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

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

takže celé toto vlákno je už pouze o tom, že idečka by měla respektovat celou cestu k formuláři! a prostě být unikátní!

to je už jediný problém, násobný render komponenty asi není takový problém řešit tím regulárním výrazem už sem si na to zvykl

mcmatak
Člen | 490
+
0
-

a ještě jednou naposledy, co teda vidím že je potřeba

  1. upravit clientscript, aby neduplikoval podmínky, ale aby se podmínky vylidace načítali v externím souboru
  2. validační pravidla clientskriptu musí mít svůj namespace tedy např. nette = {}, opět může docházet ke kolizím
  3. 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
+
0
-

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

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

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

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}
mcmatak
Člen | 490
+
0
-

tak nějak si shrnul jeden problém co je na těch dvou stránkách :)

v nette 1.x je možná vyřešeno generování id atributu elementu inputů, zkus ověřit že na stránce nejsou dvakrát stejné id atributy, ve verzi 0.9.4 je nutný stále patch

uestla
Backer | 796
+
0
-

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í).

_Martin_
Generous Backer | 679
+
0
-

A co když přijdu na stránku a než stihnu odeslat formulář, administrátor odebere zboží z eshopu? ;)

mistm
Člen | 25
+
0
-

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()

_Martin_
Generous Backer | 679
+
0
-

@mistm: Jak to myslíš: „nedostane se do metody“?

Editoval _Martin_ (15. 3. 2011 21:20)

bojovyletoun
Člen | 667
+
0
-

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

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

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

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 …