Chyba při zpracování parametrů u podkomponent komponenty

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

Pokud mám takto adresovanou hodnotu parametru, nedojde k jejímu předání do proměnné page podkomponenty grid.

?customer-grid-page=2
 \        \    \
  \        \    parametr
   \        \
    \        control
     \
      control

K chybě dochází při zpracování stavů jednotlivých komponent – někde proběhne odstranění parametru z globálního úložiště presenteru dříve, než se má parametr odstranit z úložiště a přiřadit přímo podkomponentě grid.

Pro snažší simulaci chyby posílám ukázkovou aplikaci.

romansklenar
Člen | 655
+
0
-

Nikdo nereaguje :( tak ještě posílám link na online demo s chybou.

Jak se k chybě dopracovat? Je potřeba vypnout v prohlížeči JS (nebo máznout zavináče v kódu aby to nejelo ajaxově) a dále třeba změnit třeba číslo stránky nebo řazení nad sloupcem. Pak už je chyba vidět – např. ?customer-grid-page=3 ale stránka se nezmění, vydumpujete-li si uvnitř komponenty parametr page tak zjistíte, že se do něj ta trojka nedostala.

Ajaxově se chyba neprojeví, protože se komponenta při ajaxových požadavcích po vykonání signálu nepřesměrovává pomocí redirect('this') na pročištěnou URL a tak zůstanou parametry komponenty správně přiřazeny.

vlki
Člen | 218
+
0
-

Zrovna nedávno jsem používal tvůj DataGrid právě takto jako subkomponentu a když se na to teď dívám, funguje to bez JS v pořádku.

V té aplikaci je ale použita stable verze Nette.

Takže to bude pravděpodobně bug v té vývojové.

romansklenar
Člen | 655
+
0
-

Díky vlki za nakopnutí, nepamatuješ si jaká revize to byla konkrétně? Zkoušel jsem jet až na r225, ale pořád se mi tam ta chyba projevuje. Podle changelogu se změny děly akorát v PresenterComponent, tak že by tam?

vlki
Člen | 218
+
0
-

Zrovna to testuju. Zkusil jsem v té tvé ukázkové aplikaci vyměnit Nette za to, které mám a pořád nefunguje.

Neměnil jsi nějak DataGrid? Pokud jo, postní ho a já bych ho zkusil dát do té aplikace, jestli pořád pojede v pořádku.

Osobně to využívám tak, že ta komponenta, která je mezi presenterem a DataGridem není přímo Control, ale jen PresenterComponent. V tom ale nevidím rozdíl.

romansklenar
Člen | 655
+
0
-

Nic klíčového jsem neměnil, ale poslední verze je k dispozici na SVN.

vlki
Člen | 218
+
0
-

Udělal jsem fork toho tvého příkladu, který jsem upravil tak, jak datagrid jako subkomponentu používám. Tam parametry fungují.

Není to úplně typické použítí – je to podle návrhového vzoru Servant.

Doufám, že to nějak pomůže…

http://vlki.cz/…bug_vlki.zip

Editoval vlki (15. 6. 2009 21:40)

jasir
Člen | 746
+
0
-

Ahoj, taky to zkoumám, někde je nějaký fucking bug ;)
Zatím jsem zjistil, že pokud v CustomerControl do __construct vložím (nadbytečné) $grid = $this->getComponent('grid');, vše funguje (neboli se do gridu ty parametry vloží)

vlki
Člen | 218
+
0
-

To bude asi ten problém. Komponenta není v okamžiku předávání parametrů připojená k aplikaci, proto se parametry zahodí.

Při signálu se jméno komponenty zjistí a pokusí se jí získat přes getComponent, což vyvolá tovární metodu createComponent a signál se předá dobře.

Při parametrech se ale asi getComponent nevolá. Důvod proč asi chápu – kdyby někdo předával parametr, ve kterém by byly pomlčky, tak by to po něm chtělo komponenty podle těch částí mezi pomlčkami.

JavaScript funguje bezvadně, protože tam se vše vyřeší už při požadavku se signálem, kde se komponenta připojí. Problém je až ten redirect, aby nebyly v adresním řádku nějaké do-parametry

Editoval vlki (15. 6. 2009 22:17)

romansklenar
Člen | 655
+
0
-

Paráda, aktualizoval jsem archív s demo aplikací a otestoval a vážně je to tak jak říkáte – na té úvaze s pomlčkami něco bude, teď už by to chtělo jen přímo vyjádření Davida zda-li jde o záměr nebo o chybu.

EDIT: Mám ale dojem, že je to někde v dokumentaci zmíněno, že název parametru by něměl obsahovat pomlčku, právě kvůli tomuto.

EDIT2: Tak ne, týká se to názvu komponenty, protože se pomlčka používá k oddělení názvu komponenty a signálu.

jasir
Člen | 746
+
0
-

vlki napsal(a):

To bude asi ten problém. Komponenta není v okamžiku předávání parametrů připojená k aplikaci, proto se parametry zahodí.

Ano, parametry se zahodí při saveGlobalState() a komponenta se vytváří až v dalším kroku renderTemplate().

Když cvičně dám saveGlobalState() až za renderTemplate(), vše funguje. Otázka, na kterou neumím odpovědět, je proč je saveGlobalState() před renderTemplate(), ale nějaký důvod to asi má…

romansklenar
Člen | 655
+
0
-

To taky netuším… Zkusil jsem to ale prohodit v jedné větší aplikaci, kde se komponenty vytvářejí v různých fázích a různými způsoby a zatím nepozoruju žádný defekt.

David Grudl
Nette Core | 8138
+
0
-

Předně díky za perfektně zdokumentovaný bug. Testovací aplikace, naváděcí komentáře v kódu a dokonce online demo, super!

(a omlouvám se, že jsem se dříve nedostal k reakci)


Hluboko uvnitř frameworku je zakořeněno, že oddělovač názvů komponent je spojovník. Je to přirozené (spojovník už ze svého názvu slouží ke spojování, zde názvů komponent do řetězce) a vypadá to dobře v URL. (Pomlčka sice na klávesnici není, ale klidně spojovníku říkejme pomlčka ;) )

Proto nemůže být v názvu komponenty použit spojovník.

V URL se přenášejí názvy parametrů, nikoliv komponent. Jelikož v PHP nemůže být spojovník uvnitř názvu proměnné (tedy ne snadno), lze toho využít a řetězec customer-grid-page=2 můžeme dekódovat jako parametr page komponenty grid v komponentě customer. Není potřeba závádět žádnou složitější syntax a v URL to stále vypadá dobře.

Tedy ani v názvu parametru nemůže být použit spojovník.

Suma sumárum každá komponenta může mít libovolné parametry, hierarchie může být libovolně složitá a přitom se nikde nic nebije a vypadá to (v URL) jednoduše a srozumitelně


Celé tohle chování mi i s odstupem 5 let od původního návrhu připadá nejlepší a zcela si za ním stojím. Ani bych na něm nic neměnil.

Zavrhl jsem i koncepci aliasů, pomocí které lze namapovat např. interní customer-grid-page=2 na p=2, protože taková věc má smysl leda v URL a tam to lze úspěšně řešit routerem.


Pak je tu druhá věc – životní cyklus presenteru a otázka, jak s parametry nakládat. Což je oblast, která sice funguje poměrně dobře, ale citím, že se může stále vyvíjet a vylepšovat. Což je asi případ tohoto bugreportu.

Nebudu tedy obhajovat a vysvětlovat současné chování, spíš popíšu úskalí, která je nutno respektovat.

  • generování odkazů musí být blesková a velmi dobře optimalizovaná operace
  • před započetím vykreslování stránky je potřeba stav presenteru (a všech jeho komponent) „zmrazit“. Kdyby se v polovině stránky změnil některý parametr, vygenerované odkazy by byly nekonzistentní.
  • uloží se tedy parametry všech (existujících!) komponent (zde leží zmíněný problém)
  • při generování odkazů je možné „přebít“ libovolné parametry (tj. link('...', nova-hodnota, nova-hodnota, 'param' => nova-hodnota))
  • zde je velké úskalí – co když se „přebije“ parametr, který má být uživatelsky upraven ještě metodou saveState()? Volat s každým linkem saveState je výkonostně problematické.
  • vynechávají se parametry s výchozí hodnotou
  • zde je opět úskalí: co když je jiná výchozí hodnota v handle($param = 10), v @persistent $param = 20 a ještě jiný v Route?

Jinými slovy, komponenta, která se vytvoří až ve fázi vykreslování šablony, není do zpracování zahrnuta. Neříkám, jestli je to dobře nebo špatně, ale netuším, jak to řešit jinak.

jasir
Člen | 746
+
0
-
  • před započetím vykreslování stránky je potřeba stav presenteru (a všech jeho komponent) „zmrazit“. Kdyby se v polovině stránky změnil některý parametr, vygenerované odkazy by byly nekonzistentní.

Zeptám se – co kdyby se tedy změny parametrů v render fázi $template->render() „nějak“ zakázaly? Pak by saveGlobalState() mohlo být vykonáno až po render fázi, čili by se v pořádku zpracovali i komponenty vytvářené v render fázi.
Tato chyba totiž zřejmě dost nabourává koncepci továrniček…

Editoval jasir (23. 6. 2009 17:06)

David Grudl
Nette Core | 8138
+
0
-

Ještě takto: „tím započetím vykreslování stránky“ myslím $template->render(), tedy to, co následuje až po renderXyz() nebo beforeRender().

jasir
Člen | 746
+
0
-

Ano, to jsem myslel. Čili v $template->render() by se „nějak“ zakázali změny parametrů…

romansklenar
Člen | 655
+
0
-

No to by snad jít mohlo, v Nette je FreezableObject. Hodil by se na to?

David Grudl
Nette Core | 8138
+
0
-

fixed