Chyba při zpracování parametrů u podkomponent komponenty
- romansklenar
- Člen | 655
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
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.
- romansklenar
- Člen | 655
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
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.
- vlki
- Člen | 218
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…
Editoval vlki (15. 6. 2009 21:40)
- vlki
- Člen | 218
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
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
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
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 | 8228
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ý vRoute
?
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
- 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 | 8228
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()
.