Implement UI Bootstrap tabs in sidebar - angularjs

I am using UI Bootstrap with AngularJS and I am trying to separate the vertical tabs into a sidebar using col-md-2 and col-md-10.
Currently the I have...
<uib-tabset active="activePill" vertical="true" type="pills">
<uib-tab heading="Tab 1">Vertical content 1</uib-tab>
<uib-tab heading="Tab 2">Vertical content 2</uib-tab>
<uib-tab heading="Tab 3">Vertical content 3</uib-tab>
</uib-tabset>
... which outputs ...
<div active="activePill" vertical="true" type="pills" class="ng-isolate-scope">
<ul class="nav nav-pills nav-stacked" ng-class="{'nav-stacked': vertical, 'nav-justified': justified}" ng-transclude="">
<li ng-class="[{active: active, disabled: disabled}, classes]" class="uib-tab nav-item ng-scope ng-isolate-scope active" heading="Tab 1">
Tab 1
</li>
<li ng-class="[{active: active, disabled: disabled}, classes]" class="uib-tab nav-item ng-scope ng-isolate-scope" heading="Tab 2">
Tab 2
</li>
<li ng-class="[{active: active, disabled: disabled}, classes]" class="uib-tab nav-item ng-scope ng-isolate-scope" heading="Builds">
Tab 3
</li>
</ul>
<div class="tab-content">
<!-- ngRepeat: tab in tabset.tabs -->
<div class="tab-pane ng-scope active" ng-repeat="tab in tabset.tabs" ng-class="{active: tabset.active === tab.index}" uib-tab-content-transclude="tab">
<span class="ng-scope">Vertical content 1</span>
</div>
<!-- end ngRepeat: tab in tabset.tabs -->
<div class="tab-pane ng-scope" ng-repeat="tab in tabset.tabs" ng-class="{active: tabset.active === tab.index}" uib-tab-content-transclude="tab">
<span class="ng-scope">Vertical content 2</span>
</div>
<!-- end ngRepeat: tab in tabset.tabs -->
<div class="tab-pane ng-scope" ng-repeat="tab in tabset.tabs" ng-class="{active: tabset.active === tab.index}" uib-tab-content-transclude="tab">
<span class="ng-scope">Vertical content 3</span>
</div>
<!-- end ngRepeat: tab in tabset.tabs -->
</div>
</div>
I would like to have ...
<div active="activePill" vertical="true" type="pills" class="ng-isolate-scope">
<ul class="nav nav-pills nav-stacked col-md-2" ng-class="{'nav-stacked': vertical, 'nav-justified': justified}" ng-transclude="">
<!-- snip -->
</ul>
<div class="tab-content col-md-10">
<!-- snip -->
</div>
</div>
(added col-md-2 and col-md-10)

My team once tackled this and I don't think you can position them on the side (it has to do with how the directive is structured), so we had to remove the body directives, shrink the tabs container and show state views on a different div on the right.
<div class="col-sm-2">
<uib-tabset active="active" vertical="true" type="pills">
<uib-tab heading="Tab 1" select="selected(1)"></uib-tab>
<uib-tab heading="Tab 2" select="selected(2)"></uib-tab>
<uib-tab heading="Tab 3" select="selected(3)"></uib-tab>
</uib-tabset>
</div>
<div class="col-sm-10">
<div ng-show="selectedView===1">
Show what tab 1 is selected
</div>
<div ng-show="selectedView===2">
Show what tab 2 is selected
</div>
<div ng-show="selectedView===3">
Show what tab 3 is selected
</div>
</div>
Sample plunker
This is of course a simplified case, what we did was, using ui-router to show different states on the right, that made it a more "genuine" nav-bar and we didn't have to repeat the ugly <div ng-show="selectedView===#"> markup parts.

Related

Bootstrap submenu not working consistently

I'm trying to include two submenu inside a sidebar. However, I have not been getting consistent result for the dropdown. On my 10 attempts of loading/refreshing, the sub-menu drop down only works 4/10 times. Does anyone have an idea why it is producing such results?
There's no error on console either.
Also, I'm using AngularJS to include this sidebar into my main html code.
Code for Sidebar, sidebar.html
<div class="col-md-2"><!--grid 2 for bootstrap -->
<div class="sidebar content-box" style="display: block;">
<ul class="nav">
<!-- Main menu -->
<li class="current"><i class="glyphicon glyphicon-home"></i> Dashboard</li>
<br>
<li><i class="glyphicon glyphicon-calendar"></i> Calendar</li>
<br>
<li><i class="glyphicon glyphicon-stats"></i> Stats</li>
<br>
<li class="submenu">
<a href="">
<i class="glyphicon glyphicon-list"></i> List
<span class="caret pull-right"></span>
</a>
<!-- Sub menu -->
<ul>
<li>Sub Function 1</li>
<li>Sub Function 2</li>
<li>Sub Function 3</li>
<li>Sub Function 4</li>
</ul>
</li>
<br>
<li class="submenu">
<a href="">
<i class="glyphicon glyphicon-list"></i> Function 5
<span class="caret pull-right"></span>
</a>
<!-- Sub menu -->
<ul>
<li>Sub Function 5</li>
<li>Sub Function 6</li>
<li>Sub Function 7</li>
</ul>
</li>
</ul>
</div>
</div>
AngularJS part to incldue sidebar
<!--Include header-->
<div ng-include src="'views/templates/header.html'"></div>
<!--end of header-->
<div class="page-content">
<div class="row">
<!-- Insert Side bar-->
<div ng-include src="'views/templates/sidebar.html'"></div>
<div class="col-md-10"><!--Main content -->
<div class="row">
<!-- to display content here -->
</div>
</div>
</div>
</div>
Not sure what went wrong.

How to disable/hide the tabs inside a bootstrap modal?

I have a bootstrap modal inside that I have Tabs. At present all tabs are displaying. I need to hide/disable the tabs so that at a time only one active tab is displayed. When I click on next and previous button the enabling and disabling action of tabs should take place. Kindly help me
<div class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body">
<div role="tabpanel">
<ul class="nav nav-tabs">
<li class="active">first</li>
<li>second</li>
<li>third</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab1">First tab content</div>
</div>
<md-button class="md-raised md-primary" ng-click="goNext()">Next</md-button>
<md-button class="md-warn md-raised" ng-click="goPrevious()">Previous</md-button>
<div class="tab-pane" id="tab2">Second tab content></div>
<md-button class="md-raised md-primary" ng-click="goNext()">Next</md-button>
<md-button class="md-warn md-raised" ng-click="goPrevious()">Previous</md-button>
</div>
</div>
</div>
</div>
Let's say your controller is something like this:
angular.controller('TabController', ['$scope', function($scope) {
$scope.tab = 1;
this.changeTab = function(tab) {
$scope.tab = tab;
}
}]);
You can add ng-clicks to the tabs anchors and an ng-if (or ng-show/ng-hide if you prefer) to the tab-panes, like this:
<div role="tabpanel"
ng-controller="TabController as ctrl">
<ul class="nav nav-tabs">
<li class="active">
<a href="#tab1" ng-click="ctrl.changeTab(1)">
first
</a>
</li>
<li>
<a href="#tab2" ng-click="ctrl.changeTab(2)">
second
</a>
</li>
<li>
<a href="#tab3" ng-click="ctrl.changeTab(3)">
third
</a>
</li>
</ul>
</div>
<div class="tab-content">
<div class="tab-pane active"
ng-if="tab === 1">
First tab content
</div>
<div class="tab-pane active"
ng-if="tab === 2">
Second tab content
</div>
<div class="tab-pane active"
ng-if="tab === 3">
Third tab content
</div>
</div>

AngularJS - ng-include template behaves differently

I'm struggling with understanding why ng-include template seems to break my angular application.
I have some ui bootstrap tab issues with multiple controllers and went with this design which works (note I cannot use the built in tab switching across multiple controllers, so it is hidden):
https://plnkr.co/edit/hCCVFt58OXztZDQtxGIS?p=preview
However, simply moving the nav section to a template breaks the tab switching:
https://plnkr.co/edit/Lbh2Pw2wiZAS6oZTwX47?p=preview
The variable 'active_tab' seems to update correctly... but I'm guessing the scope changes when it is in the template?
<nav class="navbar navbar-default" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" ng-click="navCollapsed = !navCollapsed">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Title</a>
</div> <!-- end navbar-header -->
<div class="collapse navbar-collapse" uib-collapse="navCollapsed">
<ul class="nav navbar-nav">
<li ng-class="{active: active_tab == 1}" ng-click="active_tab = 1">Tab 1</li>
<li ng-class="{active: active_tab == 2}" ng-click="active_tab = 2">Tab 2</li>
<li ng-class="{active: active_tab == 3}" ng-click="active_tab = 3">Tab 3</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>Selected Tab: {{ active_tab }}</li>
</ul>
</div> <!-- end uib-collapse -->
</nav> <!-- end nav -->
<uib-tabset active="active_tab">
<ng-controller ng-controller="test_controller1">
<uib-tab index="1" heading="tab 1">1: {{ tab1_content }}</uib-tab>
<uib-tab index="2" heading="tab 2">2: {{ tab2_content }}</uib-tab>
</ng-controller>
<ng-controller ng-controller="test_controller2">
<uib-tab index="3" heading="tab 3">3: {{ tab3_content }}</uib-tab>
</ng-controller>
</uib-tabset>

AngularJS - Use multiple controllers across bootstrap tabs

I'd like to divide content with bootstrap tabs. This works fine except the html breaks when I attach the ng-controller in parent elements. Is there a way to use html elements to insert ng-controller without affecting the hierarchy necessary to display tabs in bootstrap?
Working tab layout:
<div ng-app="myApp">
<div class="tab-content">
<div id="tab1" class="tab-pane fade in active">{{ tab1_content }}</div>
<div id="tab2" class="tab-pane fade in">{{ tab2_content }}</div>
<div id="tab3" class="tab-pane fade in">{{ tab3_content }}</div>
</div> <!-- all tab-content -->
</div> <!-- end myApp -->
How I'd like to use my controllers (breaks the tab displays by putting content in all tabs:
<div ng-app="myApp">
<div class="tab-content">
<ng-controller ng-controller="test_controller1">
<div id="tab1" class="tab-pane fade in active">{{ tab1_content }}</div>
<div id="tab2" class="tab-pane fade in">{{ tab2_content }}</div>
</ng-controller> <!-- end test_controller1 -->
<ng-controller ng-controller="test_controller2">
<div id="tab3" class="tab-pane fade in">{{ tab3_content }}</div>
</ng-controller> <!-- end test_controller2 -->
</div> <!-- all tab-content -->
</div> <!-- end myApp -->
Demo: https://jsfiddle.net/L2b4yLfa/
Is this acceptable?
I have initialized the same controller for two tabs. Since its a small window the code snippet output will show mobile view. click on run code snippet -> full page
angular.module('myApp', []);
angular.module('myApp').controller('test_controller1', function($scope) {
$scope.tab1_content = "Tab One Content";
$scope.tab2_content = "Tab Two Content";
});
angular.module('myApp').controller('test_controller2', function($scope) {
$scope.tab3_content = "Tab Three Content";
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<nav class="navbar navbar-default">
<div class="container-fluid">
<div class="navbar-header">
<button type="button" class="navbar-toggle pull-left" data-toggle="collapse" data-target="#myNavbar"><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button>
<a class="navbar-brand" href="#">Title</a>
</div>
<div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav">
<li class="active"><a data-toggle="tab" href="#tab1">Tab 1</a></li>
<li><a data-toggle="tab" href="#tab2">Tab 2</a></li>
<li><a data-toggle="tab" href="#tab3">Tab 3</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>Testing</li>
</ul>
</div>
</div>
</nav>
<div ng-app="myApp">
<div class="tab-content">
<div id="tab1" ng-controller="test_controller1" class="tab-pane fade in active">{{ tab1_content }}</div>
<div id="tab2" ng-controller="test_controller1" class="tab-pane fade in">{{ tab2_content }}</div>
<div id="tab3" ng-controller="test_controller2" class="tab-pane fade in">{{ tab3_content }}</div>
</div> <!-- all tab-content -->
</div> <!-- end myApp -->
So in the future you would need to share data between the same controller for the two tabs using services.
My solution was to switch to angular bootstrap ui, not use the built-in uib-tabs and use $rootscope to handle the 'active_tab' value.
<style> .uib-tab.nav-item { display:none; } .nav-tabs { border-bottom: inherit; } </style>
<div ng-app="myApp">
<nav class="navbar navbar-default" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" ng-click="navCollapsed = !navCollapsed">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Title</a>
</div> <!-- end navbar-header -->
<div class="collapse navbar-collapse" uib-collapse="navCollapsed">
<ul class="nav navbar-nav">
<li ng-class="{active: active_tab == 1}" ng-click="active_tab = 1">Tab 1</li>
<li ng-class="{active: active_tab == 2}" ng-click="active_tab = 2">Tab 2</li>
<li ng-class="{active: active_tab == 3}" ng-click="active_tab = 3">Tab 3</li>
</ul>
<ul class="nav navbar-nav navbar-right">
<li>Selected Tab: {{ active_tab }}</li>
</ul>
</div> <!-- end uib-collapse -->
</nav> <!-- end nav -->
<uib-tabset active="active_tab">
<ng-controller ng-controller="test_controller1">
<uib-tab index="1" heading="heading not displayed">1: {{ tab1_content }}</uib-tab>
<uib-tab index="2" heading="heading not displayed">2: {{ tab2_content }}</uib-tab>
</ng-controller>
<ng-controller ng-controller="test_controller2">
<uib-tab index="3" heading="heading not displayed">3: {{ tab3_content }}</uib-tab>
</ng-controller>
</uib-tabset>
</div>
Demo: https://plnkr.co/edit/36tkl3pCV7hgNz8rbpTq?p=preview

Can't add more than one Bootstrap dropdown

I have a SPA that implements modals. I'm trying to create dropdowns for certain elements that have modals attached to them. I want each dropdown to display a list of commands, and then a "link" to the modal. I can implement the modals just fine. The problem is with the dropdowns. The page works fine when I implement the first dropdown, but when I try to implement the second dropdown, neither of them work. I set the data-target attribute to see if that changed anything, but I am either not using it correctly or it's not part of the solution. Any ideas?
<div class="row cluster" ng-repeat="cluster in clusterCtrl.clusters">
<!-- this dropdown breaks the other one -->
<div class="btn-group" dropdown is-open="status.isopen" data-target="#cluster">
<h4 id="cluster" class="pointer dropdown-toggle" dropdown-toggle ng-disabled="disabled">
{{cluster.name}}
</h4>
<ul class="dropdown-menu" role="menu">
<li role="menuitem">Status</li>
<li role="menuitem">Start<li>
<li role="menuitem">Stop</li>
<li class="divider"></li>
<li role="menuitem"><a class="pointer" ng-click="openCluster()">Details</a></li>
</ul>
</div>
<!-- -->
<div class="col-lg-4">
<!-- START INSTANCE DISPLAY -->
<div class="row">
<div class="col-xs-1 instance" ng-repeat="instance in instanceCtrl.instances">
<div class="btn-group" dropdown is-open="status.isopen" data-target="#instance">
<h6 id="instance" class="pointer dropdown-toggle" dropdown-toggle ng-disabled="disabled">
{{instance.name}}
</h6>
<ul class="dropdown-menu" role="menu" aria-labelledby="single-button">
<li role="menuitem">Status</li>
<li role="menuitem">Start<li>
<li role="menuitem">Stop</li>
<li class="divider"></li>
<li role="menuitem"><a class="pointer" ng-click="openInstance()">Details</a></li>
</ul>
</div>
</div>
</div>
<!-- END INSTANCE DISPLAY -->
</div>
</div>
The data-target attribute is not needed. The problem lies in dropdown is-open="status.isopen". This is a unique attribute. To fix it, you need to write a new controller for your dropdown. Be sure not to name it DropdownController since that it an internal control to Bootstrap.
dropdown-controller.js
(function() {
angular
.module('MyApp')
.controller('DropdownCtrl', function($scope) {
$scope.status = {
isClusterOpen: false,
isInstanceOpen: false,
};
$scope.toggled = function(open) {
console.log('Dropdown is now: ', open);
};
$scope.toggleDropdown = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.status.isClusterOpen = !$scope.status.isClusterOpen;
$scope.status.isInstanceOpen = !$scope.status.isInstanceOpen;
};
});
})();
index.html
<div class="row cluster" ng-repeat="cluster in clusterCtrl.clusters">
<!-- Changed -->
<div class="btn-group" dropdown is-cluster-open="status.isClusterOpen" >
<!-- -->
<h4 id="cluster" class="pointer dropdown-toggle" dropdown-toggle ng-disabled="disabled">
{{cluster.name}}
</h4>
<ul class="dropdown-menu" role="menu">
<li role="menuitem">Status</li>
<li role="menuitem">Start<li>
<li role="menuitem">Stop</li>
<li class="divider"></li>
<li role="menuitem"><a class="pointer" ng-click="openCluster()">Details</a></li>
</ul>
</div>
<div class="col-lg-4">
<!-- START INSTANCE DISPLAY -->
<div class="row">
<div class="col-xs-1 instance" ng-repeat="instance in instanceCtrl.instances">
<!-- Changed -->
<div class="btn-group" dropdown is-instance-open="status.isInstanceOpen">
<!-- -->
<h6 id="instance" class="pointer dropdown-toggle" dropdown-toggle ng-disabled="disabled">
{{instance.name}}
</h6>
<ul class="dropdown-menu" role="menu" aria-labelledby="single-button">
<li role="menuitem">Status</li>
<li role="menuitem">Start<li>
<li role="menuitem">Stop</li>
<li class="divider"></li>
<li role="menuitem"><a class="pointer" ng-click="openInstance()">Details</a></li>
</ul>
</div>
</div>
</div>
<!-- END INSTANCE DISPLAY -->
</div>
</div>

Resources