Problem with Navbar widget and Bootstrap4 extension

Hi. I have one issue with the Navbar widget of the yii2-bootstrap4 extension (https://github.com/yiisoft/yii2-bootstrap4). Sorry if this is quite large, but, i want to describe my process so any could point me out what i am doing wrong.

I started installing yii2 through composer (basic template). After that, still using composer, i installed the extension for bootstrap4. After that I downloaded the bootstrap sources from the official web site and put them in a folder under the assets folder (@app/assets/source/bootstrap) and i made the necessary changes to the assetmanager component in @app/config/web.php to use these files for bootstrap:

'assetManager' => [
        'bundles' => [
            'yii\bootstrap4\BootstrapAsset' => [
                'sourcePath' => '@app/assets/source/bootstrap/dist',
                'css' => [
                    YII_ENV_DEV ? 'css/bootstrap.css' : 'css/bootstrap.min.css',
                ],
            ],
            'yii\bootstrap4\BootstrapPluginAsset' => [
                'sourcePath' => '@app/assets/source/bootstrap/dist',
                'js' => [
                    YII_ENV_DEV ? 'js/bootstrap.js' : 'js/bootstrap.min.js',
                ],
            ],
        ],
    ] 

Also i changed the $depends variable in @app/assets/AppAsset.php to use the extension for bootstrap4 instead of the default bootstrap3:

public $depends = [
    'yii\web\YiiAsset',
    'yii\bootstrap4\BootstrapAsset',
];

After all this, i tested the default project generated by the basic template in the browser and the top navbar was not rendering correctly. I noted that the css classes in @app/views/layout/main.php that are set in the Navbar::begin section were slightly different in bootstrap 4, so i changed them (i took the example from the Bootstrap web site):

<?php
NavBar::begin([
    'brandLabel' => Yii::$app->name,
    'brandUrl' => Yii::$app->homeUrl,
    'options' => [
        //'class' => 'navbar-inverse navbar-fixed-top', 
        'class' => 'navbar navbar-expand-lg navbar-light bg-light',
    ],
]);
echo Nav::widget([
    //'options' => ['class' => 'navbar-nav navbar-right'],
    'options' => ['class' => 'navbar-nav mr-auto'],
    'items' => [
        ['label' => 'Home', 'url' => ['/site/index'], 'options' => ['class' => 'nav-item']],
        ['label' => 'About', 'url' => ['/site/about'], 'options' => ['class' => 'nav-item']],
        ['label' => 'Contact', 'url' => ['/site/contact'], 'options' => ['class' => 'nav-item']],
        Yii::$app->user->isGuest ? (
            ['label' => 'Login', 'url' => ['/site/login'], 'options' => ['class' => 'nav-item']]
        ) : (
            '<li>'
            . Html::beginForm(['/site/logout'], 'post')
            . Html::submitButton(
                'Logout (' . Yii::$app->user->identity->username . ')',
                ['class' => 'btn btn-link logout']
            )
            . Html::endForm()
            . '</li>'
        )
    ],
]);
NavBar::end();
?>

And now the navbar is rendering as expected. But here comes my issue: If i resize the browser for testing the responsive feature, the toggle button didn’t show when the navbar items are hidden. Inspecting the code generated by the widget i found this snippet were the toggle button is supposed to be rendered:

<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#w0-collapse">
    <span class="sr-only">Toggle navigation</span>
    <span class="icon-bar"></span>
    <span class="icon-bar"></span>
    <span class="icon-bar"></span>
</button>

Verifying from the Bootstrap official documentation of the NavBar i found that the html should be:

<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#w0-collapse" aria-controls="w0-collapse" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span></button>

There is just one span element inside the button element and has the css class: “navbar-toggler-icon”. I modified the page code using chrome developer tools to verify if that is the reason of my issue and deleted all three span elements generated and put the one from Bootstrap official documentation and voila! it worked as expected.

Checking the Navbar widget code inside @app/vendor/yiisoft/yii2-bootstrap4/src/NavBar.php i found this variable:

public $togglerContent = '<span class="navbar-toggler-icon"></span>';

which is used in this function:

protected function renderToggleButton()
{
    $options = $this->togglerOptions;
    Html::addCssClass($options, ['widget' => 'navbar-toggler']);
    return Html::button(
        $this->togglerContent,
        ArrayHelper::merge($options, [
            'type' => 'button',
            'data' => [
                'toggle' => 'collapse',
                'target' => '#' . $this->collapseOptions['id'],
            ],
            'aria-controls' => $this->collapseOptions['id'],
            'aria-expanded' => 'false',
            'aria-label' => $this->screenReaderToggleText,
        ])
    );
}

which in turn is called here:

public function init()
{
    parent::init();

    /*...  all the code for render the component */

    echo $this->renderToggleButton() . "\n";
    echo Html::beginTag($collapseTag, $collapseOptions) . "\n";
}

So in theory the span element should be in the html rendered, but instead, four span elements, which i couldn’t find where are generated, are rendered in the final html. And for that, the toggle button is not showed when i resize browser.

Has somebody faced this issue? It is something i am doing wrong?

Thank you very much for your replies, and again, very sorry for the post’s length.

@Thoulah any ideas?

Firstly I don’t understand why bootstrap source was downloaded separately, as composer should already take care of this.

Secondly, icon-bar is only generated in yii2-bootstrap and not in yii2-bootstrap4. It seems the code is generated from the wrong extension.

1 Like

I took that advice from here: https://github.com/yiisoft/yii2-bootstrap4/blob/master/docs/guide/assets-setup.md in the section “Compiling from the .sass files”.

For the “icon-bar” class, the strange thing is that inspecting the html file generated, there is no reference to bootstrap3. The <link…> tag reference the folder of bootstrap4. I will double check later how the css files are included and let you know if right there is the problem.

Thank you for your replies.

Sorry for wasting your time, i am a big fat stupid :unamused:.

In phpstorm, by default the “use” section is collapsed. When i display the section there are these two lines:

use yii\bootstrap\Nav;
use yii\bootstrap\NavBar;

I changed to:

use yii\bootstrap4\Nav;
use yii\bootstrap4\NavBar;

And worked. Now is working as expected.

Thank you very much for your time.

1 Like

Glad you worked it out.
Go ahead and delete bootstrap(3). If you then find any errors you’ll know exactly what files to adjust :wink:

composer remove yiisoft/yii2-bootstrap
1 Like

Thank you. I will do that.

Abusing of your time, what is the best way to customize bootstrap through sass files in yii? Because it seems that what i did (download the sources manually) is not what is recommended.

Thank you again for your replies.

What I did is this:

assets.php

<?php

$sass = 'sass --scss --sourcemap=none -C -t compressed -I ' . Yii::getAlias('@npm/bootstrap/scss') . ' {from} {to}';

return [
    'bundles' => [
        'app\assets\AppAssetCompress',
    ],
    'cssCompressor' => $sass,
    'deleteSource' => true,
    'targets' => [
        'all' => [
            'class' => 'yii\web\AssetBundle',
            'basePath' => '@runtime/assets',
            'baseUrl' => '@web/assets',
            'css' => 'css/site.css',
        ],
    ],
    'assetManager' => [
        'basePath' => '@runtime/assets',
        'baseUrl' => '@web/assets',
        'converter' => [
            'class' => 'yii\web\AssetConverter',
            'commands' => [
                'scss' => ['css', $sass],
            ],
        ],
    ],
];

assets/AppAssetCompress.php

<?php

namespace app\assets;

use yii\web\AssetBundle;

class AppAssetCompress extends AssetBundle
{
    public $css = [
        'site.scss',
    ];

    public $js = [
    ];

    public $sourcePath = '@app/assets/css';
}

assets/css/bootstrap.scss adjust to your own needs. I disabled some function I don’t need, but this might be different for your use case.

@import "functions";
@import "variables";
@import "custom";
@import "mixins";
//@import "root";
@import "reboot";
@import "type";
@import "images";
@import "code";
@import "grid";
//@import "tables";
@import "forms";
@import "buttons";
@import "transitions";
@import "dropdown";
@import "button-group";
@import "input-group";
@import "custom-forms";
@import "nav";
@import "navbar";
@import "card";
@import "breadcrumb";
@import "pagination";
@import "badge";
//@import "jumbotron";
@import "alert";
@import "progress";
@import "media";
@import "list-group";
@import "close";
//@import "toasts";
//@import "modal";
@import "tooltip";
//@import "popover";
//@import "carousel";
//@import "spinners";
@import "utilities";
//@import "print";

assets/css/_custom.scss

This is basically a version of the bootstrap variables, adjusted with own variables and options. As it is loaded after the bootstrap variables, this allows you or override certain options. Only put options in this file that are different from the default, so you keep an overview of changes only.

assets/css/site.scss

@import "bootstrap.scss";

//Place your own Sass/CSS code in this file

Now you can create your CSS output:

./yii assets/compress assets.php /dev/null

I will not say this is the recommended way, but it works for me.

Thank you very much. Something explained like that i was looking for since forever. I will try your technique that seems to me more correct than what i was trying to do.