Jak zjistit aktuální Presenter popř. Action / View

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

Jak pls resite klasickou vec u menu:
Chcete aby po zmacknuti nejakeho hlavniho menu zustal odkaz rozsviceny
tudis zjistujeme na jakem Presenteru jsme.....

rada byla nasledujici:
Environment::getHttpRequest()->getUri()->isEqual(Presenter->link(Presenter:Action))

Jenze tady pro porovnani url musim zadat jak Presenter tak Action.
Co kdyz me ale Action nezajima? A jde mi momentalne jen o presenter.

Jak proste resite rozsviceni aktualniho menu tzn. presenteru a submenu tzn. view?

David Grudl
Nette Core | 8218
+
0
-

Nejprve – ve vrstvě presenterů by člověk neměl nikdy šahat po getUri(); pokud to potřebuje, je to spíš známka nedostatku na straně routeru.

Když jsou položky v menu tvořeny jako fully qualified action, tedy pár presenter:action, tak je lze porovnat s řetězcem $this->getName() . ':' . $this->getAction() a takto určit aktivní položku.

David Grudl
Nette Core | 8218
+
0
-

…asi rozšířím metodu getAction(), aby getAction(TRUE) vracelo přímo ten pár $this->getName() . ':' . $this->getAction(). (sice existuje $presenter->backlink(), ale to má sloužit krapet k něčemu jinému)

krissott
Člen | 48
+
0
-

… mno asi by to bylo lepší protoze $this->getName() vůbec nic nevráti kdežto $this->getAction ano…

Panda
Člen | 569
+
0
-

krissott napsal(a):

… mno asi by to bylo lepší protoze $this->getName() vůbec nic nevráti kdežto $this->getAction ano…

Mě obojí funguje úplně bez problémů. Voláš to na správném místě, tedy v kódu presenteru?

ViliamKopecky
Nette hipster | 230
+
0
-

Co udělat něco jako Uri::isEqual() pro Link? Plus aby tam bylo nastavitelné do jaké úrovně se má porovnávat, protože třeba do menu bych rád aby byla vyznačená i při změně id, ale jinde bych zase rád porovnával včetně id

romansklenar
Člen | 655
+
0
-

Já jsem řešil menu následovně:

bootstrap.php obsahuje tento router:

$router[] = new Route('<presenter>/<action>', array(
    'presenter' => 'Homepage',
	'action' => 'default',
));

controller components/MenuControl.php obsahuje logiku menu:

class MenuControl extends /*Nette\Application\*/Control
{
	/** @var string */
	public $menu_active;

	/** @var array */
	public $menu_content = array(
	    'first'   => array('zivotopis',   'Životopis'),
	    'second'  => array('sluzby',      'Služby'),
	    'third'   => array('reference',   'Reference'),
	    'fourth'  => array('rozcestnik',  'Rozcestník'),
	    'fifth'   => array('spoluprace',  'Spolupráce'),
	    'sixth'   => array('kontakt',     'Kontakt'),
	    'seventh' => array('tvorba-webu', 'Tvorba webů')
	);
	// TODO: tahat z db
	// NOTE: first, second, third ... nazvy css trid - kazda polozka je jinak podbarvena

	public function __construct($id)
	{
	    $this->menu_active = empty($id) ? 'zivotopis' : $id;
	}


	public function render()
	{
	    $template = $this->template;
	    $template->setFile(dirname(__FILE__) . '/MenuControl.phtml');
	    $template->render();
	}

	public function isActive($id)
	{
	    return $this->menu_active === $id;
	}

}

šablona menu components/MenuControl.phtml vypadá následovně:

<ul class="menu">
{foreach $control->menu_content as $k => $v}
    <li class="{$k}{if $control->isActive($v[0])} current{/if}"><h2><a href="{$v[0]}">{$v[1]}</a></h2></li>
{/foreach}
</ul>

a v presenteru HomepagePresenterse týkají změny jen metody:

public function renderDefault($id)
{
	// ... dalsi kod metody
	require_once Environment::expand('%componentsDir%/MenuControl.php');
	$this->menu = new MenuControl($id);
	$this->template->menu = $this->menu;
}

a celé to funguje tak, že když zadám prohlížeči požadavek URL_APLIKACE/?id=sluzby vysvítí/zaktivní se mi určitá položka menu.
Nevím ale jak nastavit router aby mi spolkl i tvar URL_APLIKACE/sluzby/

Chtěl bych ale upozornit, že s Nette začínám a stále si ho jen oťukávám, kdyby někoho napadl nějaký návrh jak něco udělat čistěji nebo lépe tak se s ním :)

EDIT: pro pořádek doplním že nastavení routeru pro tvar URL_APLIKACE/sluzby by mohlo vypadat takto:

$router[] = new Route('<id>', array(
	'presenter' => 'Homepage',
	'action' => 'default',
	'id' => NULL
));

Editoval romansklenar (11. 9. 2008 4:20)

phx
Člen | 651
+
0
-

Neni v tom chyba? Nemel by __constructor volat alespon puvodni constructor Controlu? Tohle by dle meho nemelo jit. Nikde nevidim provazani s prezenterem.

A jedna blba otazka: Jaky je rozdil mezi Control a Component. Zvlastni je, ze mame samy Control a je to ve slozce components.

LM
Člen | 206
+
0
-

Control je komponenta která je schopna se vykreslit.

romansklenar
Člen | 655
+
0
-

phx napsal(a):

Zvlastni je, ze mame samy Control a je to ve slozce components.

Vycházel jsem z příkladu fifteen, tam je to podobně. Ve složce components se nacházejí FifteenControl.*.
To s tím původním konstruktorem Controlu nevím, ale takhle je vše funkční a změny v prezenteru jsou v prezenteru HomepagePresenter v tom příspěvku výše.

phx
Člen | 651
+
0
-

Me slo o to, ze kdyz tvoris novou komponentu tak ji musis provazat s presenterem a nejak ji nazvat.

// $this je presenter
$comp = new XYZ($this, 'nazev);

Ale tuto logiku tim svym konstruktorem potlacujes… (viz predek Component)

David Grudl
Nette Core | 8218
+
0
-

Svázání komponenty s presenterem umožňuje:

  • používat v komponentě persistentní parametry
  • používat signály
  • volat na komponentě funkce závislé na přítomnosti presenteru (link, redirect, endSnippet)

Pokud nic z toho nepotřebujeme (nebo nechceme), není potřeba komponentu s presenterem vázat (respektive není potřeba ani dědit z PresenterComponent nebo Control). Nicméně původní konstruktor by se měl vždy rozhodně volat. Nejen v Nette.

romansklenar
Člen | 655
+
0
-

Už asi chápu, takže zkusím opravit:

components/MenuControl.php tedy:

public function __construct(IComponentContainer $parent = NULL, $name = NULL, $id = NULL)
{
	parent::__construct($parent, $name);
	$this->menu_active = empty($id) ? 'zivotopis' : $id;
}

a HomepagePresener

public function renderDefault($id)
{
	// ... nejaky kod metody
	require_once Environment::expand('%componentsDir%/MenuControl.php');
	$this->menu = new MenuControl($this, 'menu', $id);
	$this->template->menu = $this->menu;
}

Fuknční to je, zda-li správně, to nechám opět na vás :)
Z předchozích příspěvků tedy vyplývá, že v případě menu je svázání komponenty s presenterem určite na místě, jelikož můžeme presenter nechat generovat odkazy, správně?

phx
Člen | 651
+
0
-

Pokud nevyuzijes zminovane funkconsti component tak to neni nezbytne. Pak je to otazka, budouciho vyvoje zda se to vyplati ci nee.

romansklenar
Člen | 655
+
0
-

romansklenar napsal(a):

EDIT: pro pořádek doplním že nastavení routeru pro tvar URL_APLIKACE/sluzby by mohlo vypadat takto:

$router[] = new Route('<id>', array(
	'presenter' => 'Homepage',
	'action' => 'default',
	'id' => NULL
));

David Grudl napsal(a):

Svázání komponenty s presenterem umožňuje:

  • používat v komponentě persistentní parametry
  • používat signály
  • volat na komponentě funkce závislé na přítomnosti presenteru (link, redirect, endSnippet)

Když už zde bylo řečeno jaké výhody vyplývají ze zřetězením komponenty s presenterem, netušíte někdo, jak (a jestli jde) nastavit, aby komponenta přejímala router presenteru ve kterém je vytvořena?

Příklad: Vytvářím-li v šabloně odkazy

...
{$control->link('this', 'dalsi-polozka-v-menu')}
...

je to stejné jakobych v těle komponenty volal metodu link:

$this->link('this', 'dalsi-polozka-v-menu');
// dostanu tvar: URL_APLIKACE?menu-0=dalsi-polozka-v-menu

Pokud ale uvnitř komponenty zavolám toto:

$this->getPresenter()->link('this', 'dalsi-polozka-v-menu');
// dostanu chtěný tvar: URL_APLIKACE/dalsi-polozka-v-menu

a mě se jedná o to, jak nastavit metodu link(), aby ve vykreslování u šablon generovala linky tak jako druhým způsobem.

Je toto nějak v Nette řešeno? Zatím to obcházím možná trošku nešťastně

/* šablona */
...
{$control->getLink('dalsi-polozka-v-menu')}
...


/* a v těle komponenty */
public function getLink($id)
{
    return $this->getPresenter()->link('this', $id);
}

Poznámka na okraj pro přiklad: ostatní kód by zůstal stejný jako v tomto postuúpravou konstruktoru pro zřetězení s presenterus komponentou.

LM
Člen | 206
+
0
-

V šabloně komponenty můžeš použít {plink this, ‚dalsi-polozka-v-menu‘}, což vytváří odkaz přes presenter.

romansklenar
Člen | 655
+
0
-

Díky ;)

michalh
Člen | 22
+
0
-

Malé řešení pro malé stránky:

BasePresenter.php

$this->template->menu =  array(
        'About' => 'O nás',
        'Produkty' => 'Produkty',
        'Reference' => 'Reference',
        'Sluzby' => 'Služby',
        'Kontakt' => 'Kontakt'
    );

$this->template->active = $this->getName();

@layout.phtml

<ul class="menu">
    {foreach $menu as $key=>$value}
        <li class="{if $key==$active}current{/if}"><a href="{plink "$key:"}"> {$value}</a></li>
    {/foreach}
</ul>

Editoval michalh (21. 10. 2008 22:03)

kravčo
Člen | 721
+
0
-

romansklenar napsal(a):

Už asi chápu, takže zkusím opravit:

components/MenuControl.php tedy:

public function __construct(IComponentContainer $parent = NULL, $name = NULL, $id = NULL)
{
	parent::__construct($parent, $name);
	$this->menu_active = empty($id) ? 'zivotopis' : $id;
}

a HomepagePresener

public function renderDefault($id)
{
	// ... nejaky kod metody
	require_once Environment::expand('%componentsDir%/MenuControl.php');
	$this->menu = new MenuControl($this, 'menu', $id);
	$this->template->menu = $this->menu;
}

Fuknční to je, zda-li správně, to nechám opět na vás :)
Z předchozích příspěvků tedy vyplývá, že v případě menu je svázání komponenty s presenterem určite na místě, jelikož můžeme presenter nechat generovat odkazy, správně?

Pracujem teraz na jednom projekte, kde sa snažím brať veci čo najčistejšie a tak mi aj na malý projekt vzniklo dosť komponentov, ktoré toho veľa nerobia, zväčša sa akurát vykreslia podľa šablóny s pár parametrami. Prišlo mi zbytočné a dookola sa opakujúce riešiť konštruktor spôsobom:

class MyUniqueControl extends Control
{
    public function __construct(IComponentContainer $parent = NULL, $name = NULL)
    {
        $parent::__construct($parent, $name);
        // ... some init stuff
    }

    // ...
}

Našiel som si vlastný, ktorý mi ušetril kopu času a copy-paste konštruktora a samozrejme zachoval možnosť hierarchie:

abstract class MyBaseControl extends Control
{
    public function __construct(IComponentContainer $parent = NULL, $name = NULL)
    {
        $parent::__construct($parent, $name);
        $this->initialize();
    }

    public function initialize()
    {
    }
}

class MyUniqueControl extends MyBaseControl
{
    public function initialize()
    {
        // ... some init stuff
    }
}

Parametre prezentera komponente neposielam, vyťahujem ich priamo v komponente pomocou

$param = $this->getPresenter()->getParam('param');

Zatiaľ som nenarazil na situáciu, kde takéto riešenie zlyháva, ak o nejakých viete, rád sa poučím…

romansklenar
Člen | 655
+
0
-

kravco napsal(a):

Pracujem teraz na jednom projekte, kde sa snažím brať veci čo najčistejšie a tak mi aj na malý projekt vzniklo dosť komponentov, ktoré toho veľa nerobia, zväčša sa akurát vykreslia podľa šablóny s pár parametrami.

Taky mám tendence k tomu směřovat (člověk potom během chvilky udělá díky návrhu Nette z celé aplikace ajaxovou a ani to nebolí) a je to i vhodné si při více komponentách vytvářet společné předky nebo skoro-hierarchii jako s presentrama.

Mimo BaseControl třeba takový CacheableControl poslouží jako potomek lehce kešovatelná komponenta a už i podle hierarchie si může člověk domyslet co komponeta dělá a čím disponuje a hlavně se tím dodržuje heslo DRY a přehlednost kódu.

Editoval romansklenar (8. 1. 2009 2:08)

morousej
Člen | 18
+
0
-

kravco napsal(a):
Pracujem teraz na jednom projekte, kde sa snažím brať veci čo najčistejšie a tak mi aj na malý projekt >vzniklo dosť komponentov, ktoré toho veľa nerobia, zväčša sa akurát vykreslia podľa šablóny s pár >parametrami. Prišlo mi zbytočné a dookola sa opakujúce riešiť konštruktor spôsobom:

class MyUniqueControl extends Control
{
    public function __construct(IComponentContainer $parent = NULL, $name = NULL)
    {
        $parent::__construct($parent, $name);
        // ... some init stuff
    }

    // ...
}

Našiel som si vlastný, ktorý mi ušetril kopu času a copy-paste konštruktora a samozrejme zachoval možnosť hierarchie:

abstract class MyBaseControl extends Control
{
    public function __construct(IComponentContainer $parent = NULL, $name = NULL)
    {
        $parent::__construct($parent, $name);
        $this->initialize();
    }

    public function initialize()
    {
    }
}

class MyUniqueControl extends MyBaseControl
{
    public function initialize()
    {
        // ... some init stuff
    }
}

Ve starších verzích Nette na to byla funkce constructed(), která již parametry neměla, škoda, že dnes už chybí…

Editoval morousej (8. 1. 2009 12:22)

David Grudl
Nette Core | 8218
+
0
-

kravco napsal(a):

…Prišlo mi zbytočné a dookola sa opakujúce riešiť konštruktor spôsobom:

class MyUniqueControl extends Control
{
    public function __construct(IComponentContainer $parent = NULL, $name = NULL)
    {
        $parent::__construct($parent, $name);
        // ... some init stuff
    }

    // ...
}

Klidně je vynech:

class MyUniqueControl extends Control
{
    public function __construct()
    {
        parent::__construct(); // tohle sice taky není potřeba, ale dobrým zvykem je vždy rodičovský konstruktor volat
        // ... some init stuff
    }

    // ...
}

Komponentu pak do presenteru přidáš (možná i srozumitelnějším) způsobem:

	public function prepareDefault()
	{
		$control = new MyUniqueControl;
		$this->addComponent($control, 'name');

Asi to tak dám do Fifteen example, protože mi teď dochází, že tím vlastně navádím ke zbytečné komplikovanosti…

kravčo
Člen | 721
+
0
-

Je to tiež riešenie, no mne nevyhovuje. Vidím dve nevýhody oproti môjmu:

  • nesmiem zabudnúť volať konštruktor rodiča (pri initialize() je to voliteľné, štandardne je implementovaná ako prázdna preto, aby ju nebolo nutné v dedených triedach implementovať)
  • prichádzam o zažitý (štandardný) konštruktor (parent, name), navyše keď tento nový zavolám s parametrami, tieto zahodí a tým pádom komponent rodičovi nepridá… navyše bez akejkoľvek hlášky – možný zdroj fakt zlých chýb…

Editoval kravco (14. 1. 2009 0:22)

David Grudl
Nette Core | 8218
+
0
-

kravco napsal(a):

  • nesmiem zabudnúť volať konštruktor rodiča (pri initialize() je to voliteľné, štandardne je implementovaná ako prázdna preto, aby ju nebolo nutné v dedených triedach implementovať)

Nezapomeň ale, že musíš v každém pra-potomkovi volat parent::initialize().

Ještě bych chtěl upozornit na jednu důležitou věc. Komponentový model Nette umožňuje velmi dynamickou práci se stromem (komponenty můžeme vyjímat, přesouvat, přidávat), což ne vždycky šlo (vývoj třídy ComponentContainer mi trval dost přes dva roky, předávání parent&name v konstruktoru pochází z dob, kdy to jinak nešlo). Proto by byla chyba se spoléhat na to, že po vytvoření komponenty je hned znám rodič, rodič rodiče atd. Nemusí být.

tom
Člen | 171
+
0
-

Muzu se zeptat odkud se bere ta promenna $id?

romansklenar napsal(a):

public function __construct($id)
{
$this->menu_active = empty($id) ? ‚zivotopis‘ : $id;
 }

Ja kdyz si zkousim ten priklad tak ji mam stale NULL.

Diky

romansklenar
Člen | 655
+
0
-

Z url adresy (viz metoda render v tom postu a příklad url či routy).

Btw: asi bych se neřídil podle skoro 3/4 roku starých postů z dob „zero level nette experience“ ;) pomocí aktuálních vlastností šablon (bloky, porovnávání adresy) se dá napsat i rekurzivní menu na pár řádek a práce s komponentama se dost změnila, id stránky a její výchozí hodnota by se dala dát jako persistentní parametr atd…