CMenu comes with a lot of great customization options built in but sometimes you need it to do more.
Here is the Problem
I need to alter the way CMenu works and this time built in parameter settings won't help.
Here is the code I received from a designer
[html]
<ul id="search-type">
<li><a class="active" href="#s01"><span>Internet</span></a></li>
<li><a href="#s02"><span>Firms</span></a></li>
<li><a href="#s03"><span>Articles</span></a></li>
<li><a href="#s04"><span>Images</span></a></li>
<li><a href="#s05"><span>News</span></a></li>
</ul>
As you can see the active class is on the link tag and not on the list item tag.
The solution is to extend the CMenu class and Override the renderMenuRecursive on line 171 of CMenu
The first thing I want to do is stop the active class from being added to the list item tag. On line 180 of CMenu.php you will find this if statement
if($item['active'] && $this->activeCssClass!='')
$class[]=$this->activeCssClass;
I could just comment out that line and that would stop the active css class from being put into the class array and eventually $options['class']. But I want to be able to have something what will allow me to turn this feature on and off from the the CMenu widget call.
What I am going to do is start by creating a class that is going to extend CMenu. I am going to copy over the run (we need this or our extension won't run) and renderMenuRecursive functions (this is going to be where we make some code changes) and lastly I will add a public variable activateItemsOuter. The activateItemsOuter will be set to true by default so we will only have to add an activateItemsOuter parameter and set it to false when we want to use the modified way.
At this point you should have something that looks like this
<?php
Yii::import('zii.widgets.CMenu');
class MyMenu extends CMenu {
public $activateItemsOuter = true;
//need to include this for our function to run
public function run()
{
$this->renderMenu($this->items);
}
protected function renderMenuRecursive($items)
{
//... code from function
}
}
?>
Now back to line 180. We now have a parameter that we can set to true or false and we can check if we want to do it the original(default) way or the modified way.
The second thing we need to do is handle the actions for the modified way. If we flagged MyMenu to work the modified way then we need to put the active class into $item['linkOptions']['class'] and we need to make sure that if there is already a class set we need to append to that class and not overwrite it. So lets replace the whole if statement on line 180 with this new piece of code
if($item['active'] && $this->activeCssClass!='') {
// if we are on a default/true setting do it the old way
if($this->activateItemsOuter) {$class[]=$this->activeCssClass;}
// we are on the new way setting so put it in linkoptions
else {
if(isset($item['linkOptions'])) {
$item['linkOptions']= array(
'class'=>$item['linkOptions']['class'].' '.$this->activeCssClass,
);
}
else {
$item['linkOptions']= array('class'=>$this->activeCssClass);
}
}
}
Now we call our menu widget we are going to use our MyMenu class instead of CMenu and if we want the active class on the li tag then we add no extra parameters and if we want the active class on the ahref tag then we add an activateItemsOuter parameter and set it to false.
Here is an example call
<?php $this->widget('application.components.MyMenu', array(
'activateItemsOuter'=>false,
'linkLabelWrapper' => 'span',
'activateItems' => true,
'id' => 'search-type',
'items' => array(
array('label' => 'Home', 'url' => array('/site/index')),
array('label' => 'Add Your Business', 'url' => array('bdlisting/create')),
),
));
?>
Now you should be armed with the power to Adjust CMenu to suit any menu you type out there.
Here is the full class
<?php
Yii :: import('zii.widgets.CMenu');
class MyMenu extends CMenu {
// must set this to allow parameter changes in CMenu widget call
public $activateItemsOuter = true;
public function run() {
$this->renderMenu($this->items);
}
protected function renderMenuRecursive($items) {
$count = 0;
$n = count($items);
foreach ($items as $item) {
$count++;
$options = isset ($item['itemOptions']) ? $item['itemOptions'] : array();
$class = array();
if ($item['active'] && $this->activeCssClass != '') {
if ($this->activateItemsOuter) {
$class [] = $this->activeCssClass;
}
else {
if (isset ($item['linkOptions'])) {
$item['linkOptions'] = array('class' => $item['linkOptions']['class'] . ' ' . $this->activeCssClass);
}
else {
$item['linkOptions'] = array('class' => $this->activeCssClass);
}
}
}
if ($count === 1 && $this->firstItemCssClass != '')
$class [] = $this->firstItemCssClass;
if ($count === $n && $this->lastItemCssClass != '')
$class [] = $this->lastItemCssClass;
if ($class !== array()) {
if (empty ($options['class']))
$options['class'] = implode(' ', $class);
else
$options['class'] .= ' ' . implode(' ', $class);
}
echo CHtml :: openTag('li', $options);
if (isset ($item['url'])) {
$label = $this->linkLabelWrapper === null ? $item['label'] : '<' . $this->linkLabelWrapper . '>' . $item['label'] . '</' . $this->linkLabelWrapper . '>';
$menu = CHtml :: link($label, $item['url'], isset ($item['linkOptions']) ? $item['linkOptions'] : array());
}
else
$menu = CHtml :: tag('span', isset ($item['linkOptions']) ? $item['linkOptions'] : array(), $item['label']);
if (isset ($this->itemTemplate) || isset ($item['template'])) {
$template = isset ($item['template']) ? $item['template'] : $this->itemTemplate;
echo strtr($template, array('{menu}' => $menu));
}
else
echo $menu;
if (isset ($item['items']) && count($item['items'])) {
echo "\n" . CHtml :: openTag('ul', $this->submenuHtmlOptions) . "\n";
$this->renderMenuRecursive($item['items']);
echo CHtml :: closeTag('ul') . "\n";
}
echo CHtml :: closeTag('li') . "\n";
}
}
}
?>
A question..
How to customize the MyMenu to use the 'class' attribute instead of the 'id' attribute in the
<?php $this->widget('application.components.MyMenu', array( 'activateItemsOuter'=>false, 'linkLabelWrapper' => 'span', 'activateItems' => true, 'id' => 'search-type', // using class =>'search-type' fires an exception! 'items' => array( array('label' => 'Home', 'url' => array('/site/index')), array('label' => 'Add Your Business', 'url' => array('bdlisting/create')), ), )); ?>
How to Use the 'class' attribute...
One way is to add a new class variable to MyMenu.php and
override the init() function like: (this should work)
... public $cls; ... public function init() { if (isset($cls)) { $this->htmlOptions['class']=$cls; } parent::init(); }
You can then use 'cls' => 'yourclassname', in the widget.
answering the question...
"using class =>'search-type' fires an exception!"
<?php $this->widget('application.components.MyMenu', array( 'activateItemsOuter'=>false, 'linkLabelWrapper' => 'span', 'activateItems' => true, 'id' => 'search-type', // using class =>'search-type' fires an exception! 'htmlOptions' => array('class'=>'search-type'), 'items' => array( array('label' => 'Home', 'url' => array('/site/index')), array('label' => 'Add Your Business', 'url' => array('bdlisting/create')), ), )); ?>
How can i change CMenu
I need this kind of menu view, please help
<li>Cat1</li><div class="cat_count">count1</div> <li>Cat2</li><div class="cat_count">count2</div> <li>Cat3</li><div class="cat_count">count3</div>
target
set target to blank inside cmenu label
you can use
array('label' => 'FAQ', 'url' => array('site/faq'),'linkOptions'=>array('target'=>'_blank')),
How to add class or Id to specific item :
'items' => array(
array( 'label' => 'Home', 'url' => array('/site/index'), **'itemOptions'**=>array('id' => 'homeLink'), ),
http://www.yiiframework.com/wiki/726/yii-1-1-how-to-add-id-or-class-to-cmenu-items/
http://www.yiiframework.com/wiki/726/yii-1-1-how-to-add-id-or-class-to-cmenu-items/
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.