CMenu item not active any more after I used URL cheating

hi friends,

here again a bothering question:

when i add this URL cheat in my URLManager:




'look/<user:\w+>/*'=>'user/index',



and when my menu item URL was targeted at look/xyz (xyz is an user’s name) (the request line is also then: www.mysite.com/look/xyz), the page looks ok, the URL cheating works, but the menu item refuses to become active.

i know an ‘class=“active”’ option was not successfully added to the menu link. But why?

i read the CMenu class source code and didn’t find the answer.

can somebody help? what should i do to make active items appear active?

thanks in advance.

follow up.

this is the init() of CMenu widget:




	public function init()

	{

	$this->htmlOptions['id']=$this->getId();

	$route=$this->getController()->getRoute();

	$this->items=$this->normalizeItems($this->items,$route,$hasActiveChild);

	}




i understand that, because the route CMenu gets is ‘user/index’, and the item url of the menu item is ‘look/xyz’, so the method isItemActive() will never see the items active:




	protected function isItemActive($item,$route)

	{

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

		{

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

			{

				foreach(array_splice($item['url'],1) as $name=>$value)

				{

					if(!isset($_GET[$name]) || $_GET[$name]!=$value)

						return false;

				}

			}

			return true;

		}

		return false;

	}

}



but when i change the init() of CMenu to this: (let it get the requestUri instead route)




	public function init()

	{

		$this->htmlOptions['id']=$this->getId();

		//$route=$this->getController()->getRoute();

		$route=Yii::app()->request->requestUri;

		$this->items=$this->normalizeItems($this->items,$route,$hasActiveChild);

	}



The currently targeted menu link still doesn’t get active.

So this is really wierd.

CMenu is a bit rudimentary when it comes to complex menu implementations.

You would be better of passing the active item on your own if CMenu fails.

see CMenu documentation

Thanks.

Indeed, there is no ajax action in the CMenu implementations. The ‘class=active’ label is not added dynamically by the CMenu, instead it was produced when it renders the item links by checking whether the request route equals to the item url. Thus, if the route and the url differ, one can add the ‘active’=>true option to the item mannually. However, this is only applicable when one has different view file for each menu item.

Try making the CMenu link to something like:

array( ‘user/index’, array( ‘user’ => ‘xyz’ ) );

iirc, it’s the routes that need to match for the active class to kick in. My experience comes from neglecting to include the index action and it not working.

This topic is old, but just helped me to fix a problem in our project.

Our mistake was to pass an already rendered url to Cmenu. For example if you assign the result of Yii::app->createUrl() to the menu item’s url. CMenu works fine, but does not highlight the active item, if you have complex urls. Just make sure to pass an array like Say_Ten mentioned before.

The url rules are never a problem then!

I have the same problem, my class extends CMenu but doesn’t work the auto “active” on menu items.

I tried these solutions:

[list=1]

[*]SOLUTION 1: override protected function isItemActive($item, $route) but doesn’t work (assign “active” class to items that match partially with current URL)

[*]SOLUTION 2: override protected function isItemActive($item, $route) and call explicitly


$item['active']= ($this->isItemActive($item, $route))?true:false;

but doesn’t works (assign “active” class to items that match partially with current URL)

[*]SOLUTION 3: override public function init() without calling to prent::init()




public function init()

    {$this->htmlOptions['id']=$this->getId();}



this bypass the calling to


$this->items=$this->normalizeItems($this->items,$route,$hasActiveChild);

(this function probably cause the bug)

finally apply second solutions and IT WORKS!

[/list]

I report the overriding of isItemActive used in my code:




protected function isItemActive($item, $route) { 

        if (isset($item['url']) && is_array($item['url'])) {

            if(!strcasecmp(Yii::app()->createUrl($item['url'][0],  array_slice($item['url'], 1)), $route)){

                return true;

            }else return false;   

        }else return false;

    }



and this is the function into class tha extends CMenu, it returns the array with menu items:




private function returnMyItems(){

.....

  //extract the complete url

  $route = Yii::app()->getRequest()->url;

  //control if route match the URL create with $item['url']

  $item['active']= ($this->isItemActive($item, $route))?true:false;

  $items[]= $item;

.....

  return $items;

}



This solution is the only one that work for me, note that I used this configuration for main.php




....

'urlManager'=>array(

			'urlFormat'=>'path',

			'rules'=>array(	

				'<controller:\w+>/<action:\w+>/*'=>'<controller>/<action>'

			),

                        'showScriptName'=>false,

		),

....



this particular configuration uses “/*” into the rule path so all GET parameters pass to createUrl() are attached to the path ( ie $_GET[‘foo’]=1 is attached in this way controller/action/foo/1 )

Sorry for my bad english, I hope that this work may be useful