Jak zjistit aktuální Presenter popř. Action / View
- krissott
- Člen | 48
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
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
…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)
- ViliamKopecky
- Nette hipster | 230
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
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 HomepagePresenter
se 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
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.
- romansklenar
- Člen | 655
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.
- David Grudl
- Nette Core | 8218
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
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ě?
- romansklenar
- Člen | 655
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 s úpravou konstruktoru pro zřetězení s presenterus komponentou.
- michalh
- Člen | 22
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
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
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
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
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
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
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.
- romansklenar
- Člen | 655
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…