Twitter Bootstrap Navbar with AngularJS - Collapse Not Functioning

I am using Angular and Twitter Bootstrap navbar and trying to get the collapse functionality to work.

Partial: program.html

<div class="navbar navbar-inverse navbar-static-top" ng-include="'partials/navbar.html'" ng-controller="NavbarCtrl"></div>

Partial: navbar.html

<div class="navbar-inner">
    <div class="container">
        <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
        </a>
        <a class="brand" href="#">Short Course</a>
        <div class="nav-collapse collapse">
            <ul class="nav">
                <li><a href="#"><i class="icon-home icon-white"></i> Home</a></li>
                <li class="dropdown ng-class: settingsActive;">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">Intro <b class="caret"></b></a>
                    <ul class="dropdown-menu">
                        <li><a onclick='$("#myModal").modal("show");'>User Info</a></li>
                        <li><a href="#/setup">Settings</a></li>
                        <li><a href="#/get-started">Getting started</a></li>
                    </ul>
                </li>
                <li class="dropdown ng-class: programActive;" ng-controller="ProgramCtrl">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">Lessons <b class="caret"></b></a>
                    <ul class="dropdown-menu">
                        <li ng-repeat='o in lessonTypes'>
                            <a href="#/program/{{o.value}}">{{o.title}}</a>
                        </li>
                        <li class="divider"></li>
                        <li><a href="#/freeform">Free Form</a></li>
                    </ul>
                </li>
                <li class="dropdown ng-class: reportsActive;">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">Grades <b class="caret"></b></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">Simple Report</a></li>
                        <li><a href="#">Comprehensive Report</a></li>
                        <li class="divider"></li>
                        <li><a href="#">Current Grade Report</a></li>
                        <li><a href="#">Final Grade Report</a></li>
                    </ul>
                </li>
            </ul>
            <ul class="nav pull-right">
                <li><a href="#/class"><i class="icon-upload icon-white"></i> Upload/Save</a></li>
                <li><a href="#/class"><i class="icon-off icon-white"></i> Save/Logout</a></li>
            </ul>
        </div><!-- /.nav-collapse -->
    </div>
</div><!-- /navbar-inner -->

The navbar displays perfectly. The dropdowns work great. The Angular functions load the data and mark the specific items as active and populate the models perfectly. The only thing that doesn't work is the responsive collapse feature. When I resize the screen, the menu items disappear and the icon for the menu appears, but clicking on it does not work. I am stuck on this and I know it has to be a simple fix, but I just can't figure it out. Any help would be appreciated!


Solution 1:

For those interested - Here is another way of implementing this without Bootstrap's javascript.

Import Angular's UI-Bootstrap.

HTML:

<div class="navbar navbar-inverse" ng-controller="NavBarCtrl">
<div class="navbar-inner">
    <div class="container">
        <button class="btn btn-navbar" ng-click="isCollapsed = !isCollapsed"> 
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
        </button> <a class="brand" href="#">Short Course</a>
        <div class="nav-collapse" uib-collapse="isCollapsed">
            <ul class="nav">
                <li><a href="#"><i class="icon-home icon-white"></i> Home</a>
                </li>
                <li><a href="#">Lessons</a>
                </li>
                <li><a href="#">Grades</a>
                </li>
            </ul>
            <ul class="nav pull-right">
                <li><a href="#/class"><i class="icon-upload icon-white"></i> Upload/Save</a>
                </li>
                <li><a href="#/class"><i class="icon-off icon-white"></i> Save/Logout</a>
                </li>
            </ul>
        </div>
        <!-- /.nav-collapse -->
    </div>
</div>
<!-- /navbar-inner -->
</div>

JS:

var myApp = angular.module('myApp', ['ui.bootstrap']);

function NavBarCtrl($scope) {
    $scope.isCollapsed = true;
}

And the fiddle - http://jsfiddle.net/KY5Mf/

Solution 2:

I wanted to get it to work with pure AngularJS and no further JavaScript library and it turned out to be quite simple. There are only two changes needed starting with the example here (bootstrap v3):

Instead of

<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">

I used:

<button type="button" class="navbar-toggle" ng-init="isCollapsed = true" ng-click="isCollapsed = !isCollapsed">

and instead of

<div class="collapse navbar-collapse">

I used:

<div class="navbar-collapse" ng-class="{collapse: isCollapsed}">

Dropdowns

Also, if you have any dropdown menus in your navbar, the same applies to them, except the css class is not 'collapse', but 'open':

<li class="dropdown" ng-class="{ open : dd1 }" ng-init="dd1 = false" ng-click="dd1 = !dd1">

Note that multiple dropdowns will all need their own state variable on the angular root scope (if you are not using a controller). Therefore I named the first dropdown 'dd1' here, they need to be unique, otherwise multiple dropdown will open/close at the same time. (which is pretty funny, but rarely usable).

Solution 3:

This was a tricky one. The docs showed one way, and it functions great. I copied the docs example (http://twitter.github.com/bootstrap/components.html#navbar) and tried using it. I then went to the examples page and tried the layout listed here: http://twitter.github.com/bootstrap/examples/fluid.html

The one and only difference was a <button> instead of <a>

<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">

Instead of

<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">

I don't know why, but changing that made it function great.

EDIT

Arbiter pointed out that the <a /> was missing the href='#' attribute. Adding that attribute would also solve the problem.

Solution 4:

No need to engage a controller. Just use ng-init instead to initialize the isCollapsed flag when template is initially compiled.

<div class="navbar navbar-inverse" ng-controller="NavBarCtrl">
<div class="navbar-inner">
    <div class="container">
        <button class="btn btn-navbar" ng-init="isCollapsed = true" ng-click="isCollapsed = !isCollapsed"> 
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
              <span class="icon-bar"></span>
        </button> <a class="brand" href="#">Short Course</a>
        <div class="nav-collapse" collapse="isCollapsed">
            <ul class="nav">
                <li><a href="#"><i class="icon-home icon-white"></i> Home</a>
                </li>
                <li><a href="#">Lessons</a>
                </li>
                <li><a href="#">Grades</a>
                </li>
            </ul>
            <ul class="nav pull-right">
                <li><a href="#/class"><i class="icon-upload icon-white"></i> Upload/Save</a>
                </li>
                <li><a href="#/class"><i class="icon-off icon-white"></i> Save/Logout</a>
                </li>
            </ul>
        </div>
        <!-- /.nav-collapse -->
    </div>
</div>
<!-- /navbar-inner -->
</div>