Je nějaký důvod proč komponenty nemají metodu forward?
- Zax
- Člen | 370
Ahoj,
Komponenty v základu nemají pohledy, to ví každý. Nejspíš je to částečně tím, že komponenty nemají životní cyklus, tedy ani action. Zjistil jsem ale, že signály jsou celkem dostačující. Můžu udělat třeba tohle:
public function handleEdit($id) {
$this->edit = $id;
}
A v render si pošéfovat šablony. Klidně bych se vsadil, že spousta lidí (nebo aspoň začátečníků zhruba na mé úrovni) to nějak podobně používá, protože v základu to prostě jinak (asi) nejde. Problém přijde ve chvíli, kdy budu chtít udělat něco jako $this->forward(‚edit!‘, array(‚id‘=>$id));
Jasně, můžu jednoduše zavolat $this->handleEdit($id), ale co kdybych chtěl zároveň změnit nějaké persistentní parametry, třeba i v subkomponentě? Při redirectu to jde úplně v poho $this->redirect(‚edit!‘, array(‚subkomponenta-dalsisubkomponenta-parametr‘ ⇒ $hodnota)). Jistě, asi můžu ručně zavolat $this[‚subkomponenta-dalsisubkomponenta‘]->parametr = $hodnota, ale pak mám všude v komponentách jen samé:
if($this->presenter->isAjax()) {
$this['subkomponenta-dalsisubkomponenta']->parametr = $hodnota;
$this->handleEdit($id);
} else {
$this->redirect('edit!', array(
'id' => $id,
'subkomponenta-dalsisubkomponenta-parametr' => $hodnota)
);
}
Nevím jak vám, ale mně tohle připadá dost divný (jako kdyby if ajax udělej něco úplně odlišnýho, co ani nevypadá jako forward) a hlavně fakt otravný to psát pořád dokola. Lepší by bylo prostě a jednoduše tohle:
$params = array('id' => $id, 'subkomponenta-dalsisubkomponenta-parametr' => $hodnota);
if($this->presenter->isAjax()) {
$this->forward('edit!', $params);
} else {
$this->redirect('edit!', $params);
}
Neboli přibližně stejně, jako to jde v presenterech :-)
Myslím, že metoda forward by měla být součástí komponent. Napsal jsem si na to vlastní metodu, ale nejsem si až tak jistý tím, jak Nette uvnitř funguje a stopro to jde napsat líp (a neřeší to třeba parametry bez názvu). Zatím jsem však nenarazil na žádné problémy. Ideálně bych asi použil metodu $presenter->createRequest(), jenže ta metoda není public, proto přichází na scénu neobratné berličky.
// BaseControl.php
protected function hasPersistentProperty($property) {
$ref = $this->getReflection();
if($ref->hasProperty($property)) {
$refp = $ref->getProperty($property);
return $refp->isPublic() && $refp->hasAnnotation('persistent');
}
return FALSE;
}
/** Forward to signal */
protected function forward($destination, $args = array()) {
// Clean up destination
$destination = str_replace('!', '', $destination);
$anchor = strpos($destination, '#');
if(is_int($anchor)) {
$destination = substr($destination, 0, $anchor);
}
$params = array();
foreach($args as $key =>$param) {
if(strpos($key, self::NAME_SEPARATOR) > 0) {
// Subcomponent persistent param
$names = explode(self::NAME_SEPARATOR, $key);
$property = array_pop($names);
$control = $this;
foreach($names as $name) {
$control = $control->getComponent($name);
}
if($control->hasPersistentProperty($property)) {
$control->$property = $param;
}
}else if($this->hasPersistentProperty($key)) {
// Component persistent param
$this->$key = $param;
} else {
// Signal argument
$params[$key] = $param;
}
}
if($destination == 'this')
return;
$this->params = $params;
$this->signalReceived($destination);
}
Jak vidíte, je to celkem dost s*aní když chce člověk často používat obyčejný forward na signály uvnitř komponent (typicky kvůli Ajaxu) a pořád to není úplně OK. Připadám si, jako kdybych znovu vynalézal kolo (a dělal ho přitom trochu hranaté). Nějaké čisté řešení přímo v základu by dost bodlo. Ale možná je tu nějaký zjevný důvod, který nevidím, proč v základu metoda forward není?
Editoval Zax (23. 5. 2014 14:26)
- Zax
- Člen | 370
Jednoduše programuji v komponentách vše. Mám například nějaký seznam pomocí komponenty (řekněme seznam uživatelů – usersList) a ta komponenta v sobě obsahuje komponentu na stránkovadlo (usersList-paginator) a ta v sobě má persistentní parametr $page. Komponenta usersList si sama řeší třeba zobrazování přidávacího formuláře (včetně oprávnění zda aktuální uživatel smí do seznamu přidávat nové položky) a po uložení nového uživatele přes formulář chci udělat redirect(‚this‘, array(‚paginator-page‘ ⇒ $this[‚paginator‘]->lastPage)), ale i ajaxově. Zrovna mě nenapadá příklad kdy redirectuju na signál a k tomu ještě měním parametry v subkomponentách, ale prostě může se stát… A taky mi jde tak nějak i o to API, prostě zapsat forward stejným způsobem jako redirect (nad tím pak mám vlastní metodu redirectOrForward(…); která mi řeší if is ajax a volitelně i snippety).
- Zax
- Člen | 370
Redirect použít můžu, ale co Ajax? Když použiju samotný redirect, stránka se mi obnoví celá. To nechci. Chci logicky aby to směřovalo na stejné místo (chci aby aplikace fungovala stejně ať už s ajaxem nebo bez něj) a pokud možno to chci zapsat nějak jednoduše. V presenteru bych použil forward, jenže já jsem v komponentě. A nevím, jak zavolat $presenter->forward aby zohledňoval aktuální komponentu (jde to vůbec?).
- Zax
- Člen | 370
Ne, přímo v té komponentě se zobrazuje nad seznamem. Nechce se mi to popisovat celý, je to celkem na dlouho, ale obecně mi jde prostě o to, abych na všech místech, kde volám $this->redirect() v komponentách, měl možnost udělat to samé pro Ajax (tedy forward s tím, že navíc akorát zavolám redrawControl). Myslím, že je to celkem validní požadavek a v presenterech toto vyřešené je, protože tam je metoda forward. V komponentách není.
Editoval Zax (23. 5. 2014 16:48)
- Vojtěch Dobeš
- Gold Partner | 1316
Pokud potřebuješ provést forward
v komponentě, lze to
snadno:
$this->getPresenter()->forward('...');
Není to ale moc pěkné. Lepší je, aby ta komponenta poskytovala událost,
do které se může zaregistrovat presenter se svým
$this->forward('...')
voláním.
- Zax
- Člen | 370
To je ale forward v rámci presenteru, nemám pravdu? Když zavolám
$this->getPresenter()->forward('edit!', array('id'=>$id));
tak se hledá handleEdit($id) v presenteru, nikoliv v komponentě.
Hmm… momentík, právě mi asi něco došlo O_o
Fungovalo by tohle?
$fullName = $this->getUniqueId();
$this->getPresenter()->forward($fullName . '-edit!', array($fullName . '-id' => $id));
To musím vyzkoušet…
- Zax
- Člen | 370
Hmm náhodou to tak fakt funguje! Pěkný!
// BaseControl.php
protected function forward($destination, $args = array()) {
$name = $this->getUniqueId();
if($destination != 'this')
$destination = "$name-$destination";
$params = array();
foreach($args as $key => $val) {
$params["$name-$key"] = $val;
}
$this->getPresenter()->forward($destination, $params);
}
EDIT: Akorát když udělám redrawControl před forward, tak se to ignoruje, musím to psát přímo v signálu. No asi nevadí…
Editoval Zax (23. 5. 2014 21:31)