Set active item automatically

837 Views Asked by At

I have a menu in my app:

<?php
NavBar::begin([
    'brandLabel' => Html::img('@web/images/logo-top.png', ['id' => 'logo']),
    'brandUrl' => Yii::$app->homeUrl,
    'options' => [
        'class' => 'navbar-inverse navbar-static-top',
    ],
]);

if (count($menuItems)) {
    echo Nav::widget([
        'options' => ['class' => 'navbar-nav'],
        'items' => $menuItems,
    ]);
}
?>

<?php NavBar::end(); ?>

$menuItems is generated within a Controller:

private function constructMenu($categories) {
    $menu = [];

    if (is_array($categories) && (count($categories) > 0)) {
        foreach($categories as $key => $category) {
            $menu[$key] = [
                'label' => $category['name'],
                'url' => Url::to([
                    'category/view',
                    'slug' => $category['slug']
                ]),
            ];
            if (is_array($category['children']) && (count($category['children']) > 0)) {
                $menu[$key]['items'] = $this->constructMenu($category['children']);
            }
        }
    }

    return $menu;
}

Also I have urlManager config:

'urlManager' => [
    'enablePrettyUrl' => true,
    'showScriptName' => false,
    'rules' => [
        'category/<slug:[\w_-]+>' => 'category/view',
        'item/<slug:[\w_-]+>' => 'item/view',
        'cart/remove/<item_id:\d+>' => 'cart/remove',
        'cart/add/<item_id:\d+>' => 'cart/add',
    ],
],

So, the only problem is that the menu items are always active = false. How should I modify constructMenu method to set proper value for active key? Or maybe I should do it within the template?

Here is content of $menuItems from PhpStrom debug panel right before it will be passed to Nav::widget:

$menuItems variable content

Here is where I am now:

NavBar::begin([
    'brandLabel' => Html::img('@web/images/logo-top.png',
        ['id' => 'logo', 'style' => 'height: 40px; filter: invert(100%);']),
    'brandUrl' => Yii::$app->homeUrl,
    'options' => [
        'class' => 'navbar-inverse navbar-static-top',
    ],
]);

$controllerAndSlug = $this->context->id . '/' . $this->context->actionParams['slug'];
$menuItems = array_map(
    function($item) use ($controllerAndSlug) {
        $item['active'] = strpos($item['url'], $controllerAndSlug) !== false;
        return $item;
    },
    $menuItems
);

if (count($menuItems)) {
    echo Nav::widget([
        'options' => ['class' => 'navbar-nav'],
        'items' => $menuItems,
    ]);
}
NavBar::end();

It works fine, but sets active flag only on top level menu items. Now I wonder about how to pass a param to callback-function.

2

There are 2 best solutions below

2
On BEST ANSWER

Try this:

private function constructMenu($categories) {
    $menu = [];

    if (is_array($categories) && (count($categories) > 0)) {
        foreach($categories as $key => $category) {
            $menu[$key] = [
                'label' => $category['name'],
                'url' => Url::to([
                    'category/view',
                    'slug' => $category['slug']
                ]),
                'active' => Yii::$app->controller->id == 'category' 
                     && Yii::$app->controller->action->id == 'view'
                     && Yii::$app->request->get('slug') == $category['slug']
            ];
            if (is_array($category['children']) && (count($category['children']) > 0)) {
                $menu[$key]['items'] = $this->constructMenu($category['children']);
            }
        }
    }

    return $menu;
}
1
On

Although if you provide the full URL with /controller/action format it should work if it still does not, then you can use the active option of the item by checking the current controller, action, and slug

protected function constructMenu($categories) {

    $menu = [];
    $controller = Yii::$app->controller->id;
    $action = Yii::$app->controller->action->id;
    $slug = Yii::$app->request->get('slug');

    if( is_array($categories) && (count($categories) > 0) ){
        foreach( $categories as $key => $category ){
            $isActive = ($controller == 'category' && $action == 'view' && $slug == $category['slug']);
            $menu[$key] = [
                'label' => $category['name'],
                'url' => Url::to([
                    'category/view',
                    'slug' => $category['slug']
                ]),
                'active' => $isActive
            ];
            if( is_array($category['children']) && (count($category['children']) > 0) ){
                $menu[$key]['items'] = $this->constructMenu($category['children']);
            }
        }
    }

    return $menu;
}