Yii 1.1: How To Customize CMenu Output

15 followers

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

<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";
                }
            }
        }
    ?>

Total 7 comments

#17597 report it
Louis Gac at 2014/07/07 06:21am
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/

#17595 report it
Louis Gac at 2014/07/07 06:09am
How to add class or Id to specific item :

'items' => array( array( 'label' => 'Home', 'url' => array('/site/index'), 'itemOptions'=>array('id' => 'homeLink'),

   ),
#13534 report it
rajesh chaurasia at 2013/06/04 10:05am
target

set target to blank inside cmenu label you can use array('label' => 'FAQ', 'url' => array('site/faq'),'linkOptions'=>array('target'=>'_blank')),

#9702 report it
v_bogdan at 2012/09/04 10:56am
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>
#6475 report it
alejofonseca at 2012/01/12 12:03pm
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')),
    ),
));
?>
#3444 report it
bglee at 2011/04/13 12:18pm
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.

#3199 report it
ilias at 2011/03/25 09:11am
A question..

How to customize the MyMenu to use the 'class' attribute instead of the 'id' attribute in the

<

ul> field?

<?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')),
    ),
));
?>

Leave a comment

Please to leave your comment.

Write new article
  • Written by: Angelo
  • Category: Tutorials
  • Yii Version: 1.1
  • Votes: +10
  • Viewed: 52,814 times
  • Created on: Dec 4, 2010
  • Last updated: never
  • Tags: menu