Přidání beforeRender do komponent (Nette\Application\Control)
- Patrik Votoček
- Člen | 2221
Dlouho jsem přemýšlel jak elegantně a univerzálně řešit šablony v komponentách. Až jsem došel k tomu že jsem si implementoval do BaseControlu něco jako Presenter::formatTemplateFiles.
Narážím ale na takovou nepěknou věc že komponenty v zásadě nemají žádný životní cyklus. A tak není kam pořádně tuto metodu navěsit. Proto to řeším takovým ošklivím „hackem“ který se mě vůbec nelíbí.
class FooControl extends BaseControl
{
public function render()
{
$this->_render(__METHOD__);
}
}
A tak mě napadlo proč vlastně komponenty nemají žádný „životní cyklus“?
Přidání nějaké další složky do „životního cyklu“ komponenty totiž není vůbec lehké. Nette totiž volá přímo render metodu daného „view“ komponenty. Není tudíž možné toto view zjistit dříve/jinde než v dané metodě. :-(
- Ondřej Mirtes
- Člen | 1536
Komponenty nemají životní cyklus, protože jsou jednodušší, než presentery. A hlavně nemají vůbec definované, jak budou vypadat. render() na ně voláš sám, zvenku, protože víš, že danou metodu tvoje komponenta má.
Neumím si moc představit, jak takové beforeRender, nebo třeba ani action, v komponentách implementovat. Musel bys to zkrátka vždycky volat ručně.
- norbe
- Backer | 405
Já už jsem na podobný problém taky narazil a přepsal jsem si makro
widget
tak, že pokud je komponenta děděná od mojí
BaseComponent
volá se místo metody render(Xyz)
metoda run
, které se předají veškeré parametry, včetně toho,
jaká render metoda se má volat.
Je asi jasný, že v metodě run
už si můžeš životní
cyklus udělat jak potřebuješ…
- arron
- Člen | 464
On v podstate je zivotni cyklus komponenty primo napojen na zivotni cyklus
presenteru. Ten prece rozhoduje, kdy se komponenta vytvori (jestli az pri
vykreslovani sablony nebo uz driv, protoze je v ni potreba zavolat nejaky
handler atd.). Takze se tam asi bude pomerne spatne neco vymyslet uz prave
proto, ze v dobe vykresleni komponenty (controlu) neni jasne, kdy presne se
vytvorila a jestli uz se v ni neco delo nebo nedelo. Ale vrtakovo reseni mi
neprijde zase az tak spatne (viz. treba zdrojove kody Doctrine2, kde je
takovyhle veci docela dost), jenom bych pak ten render hodil do BaseControl a
udelal ho final. No a kazdy control by pak pretezoval metodu
_render
, ktera by byla deklarovana jako protected.
Mozna to vypada jako obchazeni neceho, ale rozhodne mi to neprijde, ze by to mel byt hack:-)
- Dragon Jake
- Člen | 20
v komponentách používám vlastní beforeRender()
, což je
jen protected metoda, kterou volám na začátku každého
render()
u, a vůbec se mi to nelíbí…
takže +1
- Ondřej Mirtes
- Člen | 1536
Mně se to nelíbí, potřeboval bych vědět příklad použití. Přijde mi, že se bez toho v komponentách dá obejít.
Každý píše komponenty jinak a sjednotit je nějakým životním cyklem by bylo příliš omezující.
- arron
- Člen | 464
pekelnik napsal(a):
Jo tak:-) Chtel jsem tim naznacit, ze by se takhle dal asi do metody
render()
dostat nejaky lifecycle, ne jenom tam zavolat
_render()
. A ona by se ta metoda nemusela jmenovat
_render()
, ze jo:-) Podobne jsou veci delane v Doctrine2 a prijde
mi to jako jakasi implementace navrhoveho vzoru „strategy“.
Nicmene jsem take toho nazoru, ze je to nesmysl, jen jsem se zamyslel nad pripadnym jednoduchym resenim:-)
- Filip Procházka
- Moderator | 4668
a co takhle?
class MyControl extends Control
{
public $onBeforeRender = array();
public function render($type, $param = NULL)
{
$params = func_get_args();
$type = array_shift($params);
$this->onBeforeRender($this, $type, $params);
if ($type !== NULL) {
$method = 'render'.ucfirst($type);
} else {
$method = 'renderDefault';
}
// tohle možná snad ani ne, je to hodně magické
if (!is_callable(array($this, $method))) {
return callback($this, 'renderDefault')->invokeArgs(func_get_args());
}
return callback($this, $method)->invokeArgs($params);
}
}
{control komponenta, 'begin', 'neco', 'blabla'}
{control komponenta, 'default', 'neco', 'blabla'}
{control komponenta, NULL, 'neco', 'blabla'}
{control komponenta, 'nonExisting', 'neco', 'blabla'}
zavolá se onBeforeRender
a potom tyto metody
$control['komponenta']->renderBegin('neco', 'blabla');
$control['komponenta']->renderDefault('neco', 'blabla');
$control['komponenta']->renderDefault(NULL, 'neco', 'blabla');
$control['komponenta']->renderDefault('nonExisting', 'neco', 'blabla'); // tohle je problem
//edit: takhle je to IMHO lepší :)
Editoval HosipLan (15. 10. 2010 12:21)
- Patrik Votoček
- Člen | 2221
Měl jsem na mysli něco podobného jako popisuje HosipLan.
{control comp, 'test', 1}
{control comp:Foo, 'test', 1}
class MyControl extends Control
{
protected $view;
protected $params;
protected beforeRender() { };
final public function render($view, array $params = array())
{
$this->view = $view;
$this->params = $params;
return callback($this, 'view'.ucfirst($view))->invokeArgs($params);
}
public function view($name, $id)
{
//...
}
public function viewFoo($name, $id)
{
//...
}
}
- Filip Procházka
- Moderator | 4668
S dotažením toho tvého, zkombinováním mého řešení by to mělo IMHO být velice použitelné :)
Něco jako ProspectControl
, LookoutControl
, …
:)
https://gist.github.com/627987 … neladil jsem, je to jen koncept
- Filip Procházka
- Moderator | 4668
jen mě tak napadá… strašně se mi líbí událost :D
(onBeforeRender
), ale všichni tady křičíte beforeRender() …
tak teď nevím jak si to tam implementuju :)
- o5
- Člen | 416
Tohle se asi nikam nepohlo, nebo?
Resim tedka jednoduchou vec v mojim Gridu a to abych nemel duplnicitni obsah, kdyz do url dam treba grid-page=100 pricemz maximalne mozne je napr. 50. K tomuhle ifu potrebuju vedet kolik je total count, tedy nekdy ve fazi, kde uz mam ziskane vysledky → tuhle fazi mam v metode render(), jenze tam uz je pozde delat redirect. Tak jsem si rikal ze to presunu do loadState() ovsem to je zase brzo, protoze handle metody se volaji po loadState() a je zbytecne generovat vysledek, kdyz napr. v handleFilterForm() udelam znovu redirect..
Potreboval bych opravu asi nejaky beforeRender() v controlu..
Nejaky napady? :)
Editoval o5 (22. 4. 2011 18:24)
- Filip Procházka
- Moderator | 4668
Pak tě bude zajímat attached()
.
class MyComponent exteds Nette\Application\UI\Control
{
protected function attached($obj)
{
parent::attached($obj);
if (!$obj instanceof Nette\Application\UI\Presenter) {
return;
}
// tady je komponenta připojena k presenteru (na zanoření nezáleží)
dump('první');
}
public function handleSomething()
{
// volá se až po připojení
dump('druhé');
}
}
- tany
- Člen | 31
knyttl napsal(a):
No já bych spíš potřeboval něco jako „onBeforeHandleSignals“ – protože handleSignals probíhá ještě dříve než render…
Přesně problém, co tu mám taky .. bohatě by stačilo předávat
parametry __construct metodě, místo render.
Jakmile komponenta třeba maže položku, tak jí musím znovu předávat jako
parametr nějaké id, protože render($id) se volá až po handle.
Sice by se to dalo řešit jinak, ale ten luxus, že mám v šabloně jen
{snippet pluginFavorites}
{control pluginFavorites:full,$guid}
{/snippet}
a komponenta se chová kompletně jako vlastní aplikace, je jeden z máka důvodů, proč sem nepověsil php na hřebík.
Editoval tany (22. 12. 2011 6:18)