CMenu and Active menu items

Here is an example of one of my CMenu list items:


array('label'=>'News', 'url'=>array('/news/index'), 'active'=>Yii::app()->controller->id=='news'),

So here I have to specify that the menu item has active class if the controller id is news. So this also keeps the menu item active for url /news/view

However I recall in the previous menu implementation in Yii 1.0 (MainMenu widget) it did this automatically, i.e. it determines what the controller is based on the link url/route (so you do not need to specify ‘active’ property). The menu item is active for all of its own actions without having to specify the ‘active’ property.

Any reason why this functionality would have been removed from CMenu widget in Yii 1.1?

According to the documentation http://www.yiiframework.com/doc/api/CMenu#items-detail

Yes that’s right. That basically means if I specify ‘url’=>array(’/news/index’) and do not specify the active property then the list item will be active for this URL, but ONLY this URL.

In MainMenu widget the item would be active for all of its controller actions. For example:


array('label'=>'Users, 'url'=>array('/user')), 

In the view the item would be active for all ‘User’ controller actions.

That’s not quite right. The MainMenu required both the url and all _GET parameters to be equal (only the order did not matter) to those defined for the menu item.

So your example would only highlight the ‘Users’ menu item if the url equals http:://mydomain.xxx?r=/user. If a _GET parameter is added or the route is expanded (e.g. r=/user/view), then the ‘Users’ menu is no longer highlighted.

CMenu works like that by default.

MainMenu widget in Yii 1.1.0:

array(‘label’=>‘Users, ‘url’=>array(’/user’)),

This keeps the list item active for URLs:

/user/admin

/user/list

/user/view/id/x

/user/update/id/x

/user/create

As well as just /user. The advantage is that I did not have to specify the ‘active’ property to tell it to stay active on its own controller actions.

Hmm… GSTAR method doesnt work on me. Maybe I should use url rewrite instead of the default index.php?controller/action&id or maybe it works if I use nested CMenu instead of separate submenu on different instance of CMenu?

GSTAR method doesnt work on me too.

Did url rewrite works?




array('label'=>'管理首页', 'url'=>array('/admin/default/index'),'active'=>$this->id=='default'?true:false),

array('label'=>'文章管理', 'url'=>array('/admin/articles/admin'),'active'=>$this->id=='articles'?true:false),



yeah this one works

@GSTAR doesn’t work for me according to your post. (yii 1.1.x)

For me GSTAR’s solution worked:


'active'=>Yii::app()->controller->id == 'go'

When I used yours (no matter, if with or without “?true:false” part) I got error saying: “Using $this when not in object context”. Probably, because my menu generation is involved into few functions and isn’t just a plain array.

This is what I did:

I created a function:




function isItemActive ($route,$id)

	{

		//explode the route ($route format example: /site/contact)

		$menu = explode("/",$route);

		

		//compare the first array element to the $id passed

		return $menu[0] == $id ? true:false;

	}



and this is called like:


<?php $this->widget('zii.widgets.CMenu',array(

				'items'=>array(

					array('label'=>'Home', 'url'=>array('/site/index'), 'visible'=>!app()->user->isGuest),

					array('label'=>'About', 'url'=>array('/site/page', 'view'=>'about'), 'visible'=>!app()->user->isGuest),

					array('label'=>'Contact', 'url'=>array('/site/contact'), 'visible'=>!app()->user->isGuest),

					array('label'=>'Rights', 'url'=>array('/rights'), 'visible'=>!app()->user->isGuest,'active'=>isItemActive($this->route,'rights')),

					array('label'=>'Users', 'url'=>array('/user'), 'visible'=>!app()->user->isGuest,'active'=>isItemActive($this->route,'user')),

					array('label'=>'Login', 'url'=>array('/user/login'), 'visible'=>app()->user->isGuest),

					array('label'=>'Logout ('.app()->user->name.')', 'url'=>array('/user/logout'), 'visible'=>!app()->user->isGuest)

				),

			)); ?>

This can be configured to fit your needs.

Works fine for me :)

Thanks for your solution. But - as I mentioned in a similar thread - I can’t use it in my current project. I split menu generation into two separate files and involved a few functions to generate it. At some point of doing so I had to make some some logic or organisational mistake as right now I can’t use $this in active, because I’m getting error saying that I’m trying to use $this in non-object context. Since GSTAR’s solution is working for me right now and I’m bloated with many, many other problems to be solved ASAP in current project I moved looking for what is causing this in to a bit later part of my project timeline.

But thanks again (and +1) for bringing another solution that can be useful in some other projects or can be reused by other developers.

Thank you! This is my first reputation point!

Then congratulations and keep going this way, only up! :]

Thanks, this has been a useful reference …

Solved this problem without additional "active" params in url

In CMenu.php replace isItemActive method with following code




	protected function isItemActive($item,$route)

	{

            $item['url'][0] = trim($item['url'][0],"/")."/";

            $src = explode("/", $item['url'][0]);

            $route = explode("/", $route);


		if(isset($item['url']) && is_array($item['url']) && ($src[0]==$route[0]) /* && (!strcasecmp($item['url'][0],$route) )*/ )

		{

			if(count($item['url'])>1)

			{

.........



Hi Brain,

In short points:

  1. Since this is your first post, welcome to the forum and happy Yii-ing! :]

  2. Even if your code works fine, it is a very bad idea to change anything in the framework core code! In your situation, an extension to CMenu, an own class descending from it would be an option. If it is worth doing so (and I don’t see reasons).

  3. There are guys out there, in a Dev Team, that are working on Yii for nearly 2,5 year now, I think they’re a little bit more experienced coders than you, and believe me - if they would find a better solution then using isMenuActive they would do this. First reason, for leaving it as it is, that comes to my mind is object-oriented programming idea (essence) that is - extendibility. With current implementation one can extend base CMenu class and write own, completely different implementation of this function. Leaving it your way makes other not possible to use this approach.

Cheers,

Trejder

I know this an old thread, but this is ontopic.

I have a "user" module. Using the standard structure in the default layout file generated by Yii, the following does not work for a module, and no "active" class is added to the link when viewing the page:


array('label'=>'Profile', 'url'=>array('/user/profile'), 'visible'=>!Yii::app()->user->isGuest),

Using latest Yii from SVN.

Anybody know the fix?

edit

Ok, this works, but as far as I am concerned the fact the above doesn’t, its a bug in cMenu:


array('label'=>'Profile', 'url'=>array('/user/profile'), 'visible'=>!Yii::app()->user->isGuest), 'active'=>$this->action->id=='profile'),

Case insensetive comparison needed, then it should work in both methods, e.g.:




...

array('label'=>'Partner type', 'url'=>array('/PartnerType/index'), 'visible'=>$isAdmin, 'active'=> (strcasecmp($this->id, 'partnertype') === 0)  ? true : false),

...



and this:




...

array('label'=>'Partner type', 'url'=>array('/PartnerType/index'), 'visible'=>$isAdmin, 'active'=> (strcasecmp(Yii::app()->controller->id, 'partnertype') === 0)  ? true : false),

...



should produce the same result.

Controller identificators might be converted to lowercase to provide unique mapping (keys of associative arrays are used for mapping).

Using mbmenu I was having trouble getting the parent of a dropdown children stay active when using static pages as defined at: yiiframework: how-to-display-static-pages-in-yii/.

Using $this->id to match the page wasn’t working because I was using subfolders like the example: example.com/index.php?r=site/page&view=help.contact

I was able to get it working though by doing something similar:

First make sure function is available within $this and set a variable:


$current = isset($this->getAction()->view)?$this->getAction()->view:$this->id;

Use $current to set active:


$this->widget('application.extensions.mbmenu.MbMenu', array(

		    'items' => array(

			array('label' => 'Tutorials', 'url' => array('/site/page/&view=tutorials.index'), 

                                         'visible' => !Yii::app()->user->isGuest,'active'=>strpos($current,'/tutorials/')?true:false, 

			                    'items' => array(

					               array('label' => 'Data Migration', 'url' => array('/site/page/&view=tutorials.fsf_migration')),

					               array('label' => 'Custom Thumbnails', 'url' => array('/site/page/&view=tutorials.custom_thumbs')),

					               array('label' => 'Useful VBS Scripts', 'url' => array('/site/page/&view=tutorials.vbs')),

			    ),),

		    ),

		));