Přidání beforeRender do komponent (Nette\Application\Control)

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

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

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

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

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

pekelnik
Člen | 462
+
0
-

@arron kristepane jenom to ne!

@vrtak beforeRender v komponentách taky postrádám. BaseControl řeší formátování šablony. Na konci metody render[XXX] kde připravím data – zavolám parent::render() – která provede vlastní vykreslení.

Resumé: beforeRender pro komponenty: +1

Dragon Jake
Člen | 20
+
0
-

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

redhead
Člen | 1313
+
0
-

+1

Honza Marek
Člen | 1664
+
0
-

A kdo by to beforeRender volal? Makro widget/control?

arron
Člen | 464
+
0
-

pekelnik napsal(a):

@arron kristepane jenom to ne!

„Kristepane jenom ne“ co presne ? :-) Zajima me, co je na tom spatneho, abych to tak uz nikdy nedelal :-)

pekelnik
Člen | 462
+
0
-

@arron měl jsem na mysli render kterej volá _render

Ondřej Mirtes
Člen | 1536
+
0
-

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

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

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

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

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

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

jtousek
Člen | 951
+
0
-

Nevím přesně co to tu vymýšlíte a jsem línej to celé číst, ale pro vylepšení komponent jsem všema deseti takže +1. :D

Filip Procházka
Moderator | 4668
+
0
-

Včera se mi chtělo… https://gist.github.com/627987 ;-)

o5
Člen | 416
+
0
-

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

Kam by se to mělo hýbat? Řešení existuje a funguje.

knyttl
Člen | 196
+
0
-

No já bych spíš potřeboval něco jako „onBeforeHandleSignals“ – protože handleSignals probíhá ještě dříve než render…

Filip Procházka
Moderator | 4668
+
0
-

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é');
	}

}
knyttl
Člen | 196
+
0
-

Teď jsem to zrovna objevil. Každopádně to není úplně intuitivní. Ale aspoň to :-)

tany
Člen | 31
+
0
-

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)