Uib-Dropdown Does not work in Body of HTML View - javascript

I have installed, correctly, the following:
"ui-bootstrap": "0.12.2",
"ngAnimate": "1.5.5",
"AngularJs": "1.5.5
When creating a dropdown menu in the body tag of my html view, I am not receiving any visible results. I am not receiving any error messages in both console and network analyzer. Also, all other angularJS and code work perfectly in the view, so there is nothing wrong with external set-up. Here is the HTML in Question:
<div class="btn-group" uib-dropdown dropdown-append-to-body>
<button id="btn-append-to-body" type="button" class="btn btn-primary" uib-dropdown-toggle>
DropDown <span class="caret"></span>
</button>
<ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="btn-append-to-body">
<li role="menuitem">Action</li>
<li role="menuitem">Another action</li>
<li role="menuitem">Something else here</li>
<li class="divider"></li>
<li role="menuitem">Separated link</li>
</ul>
</div>
And Here is JS in Question:
var myApp = angular.module('app', ['ui.bootstrap','ngAnimate']);
myApp.controller('Controller', ['$scope', '$http', '$log', function($scope, $http, $log) {
$scope.status = {
isopen: false
};
$scope.toggled = function(open) {
$log.log('Dropdown is now: ', open);
};
$scope.toggleDropdown = function($event) {
$event.preventDefault();
$event.stopPropagation();
$scope.status.isopen = !$scope.status.isopen;
};
$scope.appendToEl = angular.element(document.querySelector('#dropdown-long-content'));
}]);

As of ui-bootstrap 0.14.0, they require the uib- prefix for their directives. It looks like you're using an earlier version so you should get the directive to show by removing the uib- prefix. In other words, "uib-dropdown-menu" becomes "dropdown-menu" etc.
Either that, or use the latest version of ui-bootstrap.
Source: bootstrap migration guide for prefixes

Related

Angular function not running in controller or binding to $scope variable

I am sure this is something simple, but I have tried this numerous ways with no result.
I have a factory that retrieves profile data from a simple endpoint, /api/me. It should return name, email, etc.... and it works fine for me profile page. However, my navbar seems to be a different story.
Some functions in the navBarCtrl seems to kick in, but it simply returns a boolean value. The function I REALLY want to kick in, seems to be inert. It could be because it's waiting for some sort of event.
I attempted to use a broadcast when the auth service checks to see if the user is authenticated, but this didn't seem to do it either.
First, the Node API endpoint. Probably not necessary, but just in case.
app.get('/api/me', ensureAuthenticated, function(req, res) {
User.findById(req.user, function(err, user) {
res.send(user);
});
});
The Account factory.
angular
.module('issuefy')
.factory('Account', Account);
Account.$inject = ['$http', 'LocalStorage'];
function Account($http, LocalStorage) {
return {
getUser: function() {
return $http.get('/api/me');
},
updateProfile: function(profileData) {
return $http.put('/api/me', profileData);
}
};
Now the controller. Note, I attempt to do it two diffent ways.
angular
.module('issuefy')
.controller('NavbarCtrl', NavbarCtrl);
NavbarCtrl.$inject = ['$scope', '$http', '$location', 'LocalStorage', 'QueryService', '$auth', 'toastr', 'Account'];
function NavbarCtrl($scope, $http, $location, LocalStorage, QueryService, $auth, toastr, Account) {
$scope.getNavUser = function() {
Account.getUser()
.then(function(response) {
$scope.userNav = response.data;
})
.catch(function(response) {
toastr.error(response.data.message, response.status);
});
};
$scope.$on('user-authenticated', function(event, args) {
Account.getUser()
.then(function(response) {
$scope.userNav1 = response.data;
})
.catch(function(response) {
toastr.error(response.data.message, response.status);
});
});
$scope.isAuthenticated = function() {
return $auth.isAuthenticated();
};
}
Now the template page.
<div ng-controller="NavbarCtrl" class="navbar navbar-default navbar-static-top">
<div class="navbar-header">
<a class="navbar-brand" href="/"><i class="ion-ios7-pulse-strong"></i> Issuefy</a>
</div>
<ul class="nav navbar-nav">
<li>Home</li>
<li ng-if="isAuthenticated()">Profile</li>
</ul>
<ul ng-if="!isAuthenticated()" class="nav navbar-nav pull-right">
<li>Login</li>
<li>Sign up</li>
{{user.name}}
</ul>
<ul ng-if="isAuthenticated()" class="nav navbar-nav pull-right">
<li>Logout</li>
<li><span ng-bind="userNav.email"></span></li>
<li><span ng-bind="userNav1.email"></span></li>
<li>{{userNav1.email}}</li>
<li>
<div class="btn-group" uib-dropdown is-open="status.isopen">
<button id="single-button" type="button" class="btn btn-primary" uib-dropdown-toggle ng-disabled="disabled">
<i class="fa fa-user">a {{userNav.email}} email</i></span>
</button>
<ul class="uib-dropdown-menu" role="menu" aria-labelledby="single-button">
<li role="menuitem">Action</li>
<li role="menuitem">Another action</li>
<li role="menuitem">Something else here</li>
<li class="divider"></li>
<li role="menuitem">Separated link</li>
</ul>
</div>
</li>
</ul>
</div>
<div ui-view></div>
What am I missing? I am sure it's something simple.
I want to know what return $auth.isAuthenticated();, is it sync or async? If it is async, then the expression isAuthenticated() maybe return the wrong value.

Enable or disable links when a route parameter is present or isn't present

I have routing that looks like this (removed the second param in when() to make it easier to read):
app.config(function($routeProvider, $locationProvider){
// Prepare for html5 mode
$locationProvider.hashPrefix('!');
$locationProvider.html5Mode(true);
// Setup routing
$routeProvider.when('/dashboard/main', {
}).when('/dashboard/website/:id/clicks', {
}).when('/dashboard/website/:id/uploads', {
}).when('/dashboard/website/:id/forms', {
}).when('/dashboard/website/:id', {
}).otherwise({
redirectTo: '/dashboard/main'
});
});
I then have navigation links, which I would then like to disable when :id is not present.
<ul class="nav nav-pills nav-stacked">
<!-- always active -->
<li>
<i class="glyphicon glyphicon-home"></i> Home
</li>
<!-- active only if :id is set -->
<li>
<i class="glyphicon glyphicon-hand-up"></i> Clicks
</li>
...
</ul>
How can I make the second item enable itself when :id is present and disable itself when :id is not present?
You can use $routeParams inside the controller and check:
$scope.Options = $routeParams.id !== 'undefined'
And change the view to
<li ng-show='Options'>
<i class="glyphicon glyphicon-hand-up"></i> Clicks
</li>
You can inject $routeParams in your controller as
app.controller('yourControllerName', ['$routeParams', '$scope', function($routeParams, $scope){
$scope.isActive = inactive;
if($routeParams.id){
$scope.isActive = active;
}
}]);
In your mark up,
<li data-ng-class="isActive">
<i class="glyphicon glyphicon-hand-up"></i> Clicks
</li>
Then provide suitable styles for .inactive and .active classes using CSS

Dynamicly added dropdown menu in AngularJS + Bootstrap

I'm writing a module that will create a dynamic menu on the fly. How to run a directive after adding new <li> with css class dropdown which is also added by ng-class.
The code:
angular.module('myapp', ['ui.bootstrap'])
.factory("menuService", ["$rootScope", function($rootScope) {
"use strict";
return {
menu: function() {
$rootScope.globalMenu;
},
setMenu: function(menu) {
$rootScope.globalMenu = menu;
}
};
}])
.controller("MainController", ["$scope", "menuService",
function($scope, menuService){
menuService.setMenu([{href:"#", label:"Dropdown",
dropdown:[{href:"/edit", label:"Edit"}]},
{href:'/', label:'test'}]);
$scope.bodyText = "Some text";
}]);
This is the code in html
<ul class="navbar-nav nav navbar-left">
<li ng-repeat="menu_element in globalMenu" ng-class="{dropdown: menu_element.dropdown != undefined}">
<a ng-href="{{menu_element.href}}" ng-class="{'dropdown-toggle': menu_element.dropdown != undefined}">
{{menu_element.label}}
<b class="caret" ng-if="menu_element.dropdown != undefined"></b>
</a>
<ul ng-if="menu_element.dropdown != undefined" class="dropdown-menu">
<li ng-repeat="sub_element in $parent.menu_element.dropdown">
<a ng-href="{{sub_element.href}}">{{sub_element.label}}</a>
</li>
</ul>
</li>
</ul>
Link to plunker:
http://plnkr.co/edit/pgH35mmsjLJqV4yJuSYq?p=preview
So what I want to do is the same or similar as for jQuery, there I would run $("li.dropdown").dropdown() after adding whole ul>li blocks. I'm new to Angular and I want to make this in the angular way.
I read about directives, how to use them. But I couldn't find how to apply directive in runtime. I've read about transclude: element in a directive (ui.bootstrap.dropdownToggle) doesn't have it enabled. I'm sure that there is a easy way, but couldn't find it myself...
Solved!
I've finally made it with ng-if and ng-repeat-start. With help in comments, I've found that ng-class does not run directives.
<ul class="navbar-nav nav navbar-left">
<span ng-repeat-start="menu_element in globalMenu"></span>
<li ng-if="menu_element.dropdown !== undefined">
<a ng-href="{{menu_element.href}}" class="dropdown-toggle">
{{menu_element.label}}
<b class="caret" ></b>
</a>
<ul class="dropdown-menu">
<li ng-repeat="sub_element in $parent.menu_element.dropdown">
<a ng-href="{{sub_element.href}}">{{sub_element.label}}</a>
</li>
</ul>
</li>
<li ng-if="menu_element.dropdown === undefined">
<a ng-href="{{menu_element.href}}">
{{menu_element.label}}
</a>
</li>
<span ng-repeat-end></span>
</ul>
Working example on Plnkr. Something happened with the css on Plunker, yesterday it was working... but still it works.
Nice, this helped me along the way. I've a slight variation on your theme.
<ul class="nav navbar-nav">
<li ng-repeat='link in menu track by $index' ng-class='[{dropdown:link.sub}]'>
/* normal menu */
<a ng-if='!link.sub' ng-bind='link.id' ng-click='jump(link.to)'></a>
/* dropdown menu */
<a ng-if='link.sub' class="dropdown-toggle" data-toggle="dropdown">
<span ng-bind='link.id'></span>
<ul class="dropdown-menu inverse-dropdown">
/* and repeat for the submenu */
<li ng-repeat='slink in link.menu track by $index'>
<a ng-bind='slink.id' ng-click='jump(slink.to)'></a>
</li>
</ul>
</a>
</li>
</ul>
My menu array is a list of
{id:'name', to:n}
where n points to an array listing some html I push into the page. When there is a sub menu the menu array element is
{id:'name', sub:true, menu:[{id:'name', to:n}, etc.]}
I tried ui-bootstrap but never got my head around it.

Angularjs optional navigation

I have an angular app with 11 pages. Some of them have a nav which is using this code:
<div class="well sidebar-nav" ng-app="navList">
<ul class="nav nav-list" ng-controller="navCtrl">
<li ng-class="navClass('home')"><a href='#/home'>Home</a></li>
<li ng-class="navClass('about')"><a href='#/about'>About Us</a></li>
<li ng-class="navClass('contact')"><a href='#/contact'>Contact Us</a></li>
</ul>
</div>
var navList = angular.module('navList', []);
navList.controller('navCtrl', ['$scope', '$location', function ($scope, $location) {
$scope.navClass = function (page) {
var currentRoute = $location.path().substring(1) || 'home';
return page === currentRoute ? 'active' : '';
};
}]);
I don't want this nav on all the pages. What is best solution to handle this situation? Should I show hide DOM element or do I need to remove it when not required?
It's entirely up to you. As #Chandermani said, there's not problem hiding it if your DOM is small. This is how most web-apps that have a fluid layout work. And given the size of your navigation, that's really not going to pose much of a problem.
If you did want to remove it entirely though (which is also fine) you could get angular to do this with the ng-switch directive. On your outer element that contains the code you posted, you add the switch, then on the well class element above, you add your condition, so for example, you may have
<div class="wrapper" ng-switch="smallScreen" ng-app="navList">
<div class="well sidebar-nav" ng-switch-when="true">
<ul class="nav nav-list" ng-controller="navCtrl">
<li ng-class="navClass('home')"><a href='#/home'>Home</a></li>
<li ng-class="navClass('about')"><a href='#/about'>About Us</a></li>
<li ng-class="navClass('contact')"><a href='#/contact'>Contact Us</a></li>
</ul>
</div>
</div>
Then in your controller you'll need a scope variable called smallScreen which you set to true or false depending on whether you want to see it.
On another note, it's unusual to have the ng-app directive attached to a menu or was this just for demonstration?

How to set bootstrap navbar active class with Angular JS?

If I have a navbar in bootstrap with the items
Home | About | Contact
How do I set the active class for each menu item when they are active? That is, how can I set class="active" when the angular route is at
#/ for home
#/about for the about page
#/contact for the contact page
A very elegant way is to use ng-controller to run a single controller outside of the ng-view:
<div class="collapse navbar-collapse" ng-controller="HeaderController">
<ul class="nav navbar-nav">
<li ng-class="{ active: isActive('/')}">Home</li>
<li ng-class="{ active: isActive('/dogs')}">Dogs</li>
<li ng-class="{ active: isActive('/cats')}">Cats</li>
</ul>
</div>
<div ng-view></div>
and include in controllers.js:
function HeaderController($scope, $location)
{
$scope.isActive = function (viewLocation) {
return viewLocation === $location.path();
};
}
I just wrote a directive to handle this, so you can simply add the attribute bs-active-link to the parent <ul> element, and any time the route changed, it will find the matching link, and add the active class to the corresponding <li>.
You can see it in action here: http://jsfiddle.net/8mcedv3b/
Example HTML:
<ul class="nav navbar-nav" bs-active-link>
<li>Home</li>
<li>Contact</li>
</ul>
Javascript:
angular.module('appName')
.directive('bsActiveLink', ['$location', function ($location) {
return {
restrict: 'A', //use as attribute
replace: false,
link: function (scope, elem) {
//after the route has changed
scope.$on("$routeChangeSuccess", function () {
var hrefs = ['/#' + $location.path(),
'#' + $location.path(), //html5: false
$location.path()]; //html5: true
angular.forEach(elem.find('a'), function (a) {
a = angular.element(a);
if (-1 !== hrefs.indexOf(a.attr('href'))) {
a.parent().addClass('active');
} else {
a.parent().removeClass('active');
};
});
});
}
}
}]);
You can have a look at AngularStrap, the navbar directive seems to be what you are looking for:
https://github.com/mgcrea/angular-strap/blob/master/src/navbar/navbar.js
.directive('bsNavbar', function($location) {
'use strict';
return {
restrict: 'A',
link: function postLink(scope, element, attrs, controller) {
// Watch for the $location
scope.$watch(function() {
return $location.path();
}, function(newValue, oldValue) {
$('li[data-match-route]', element).each(function(k, li) {
var $li = angular.element(li),
// data('match-rout') does not work with dynamic attributes
pattern = $li.attr('data-match-route'),
regexp = new RegExp('^' + pattern + '$', ['i']);
if(regexp.test(newValue)) {
$li.addClass('active');
} else {
$li.removeClass('active');
}
});
});
}
};
});
To use this directive:
Download AngularStrap from http://mgcrea.github.io/angular-strap/
Include the script on your page after bootstrap.js:
<script src="lib/angular-strap.js"></script>
Add the directives to your module:
angular.module('myApp', ['$strap.directives'])
Add the directive to your navbar:
<div class="navbar" bs-navbar>
Add regexes on each nav item:
<li data-match-route="/about">About</li>
Here's a simple approach that works well with Angular.
<ul class="nav navbar-nav">
<li ng-class="{ active: isActive('/View1') }">View 1</li>
<li ng-class="{ active: isActive('/View2') }">View 2</li>
<li ng-class="{ active: isActive('/View3') }">View 3</li>
</ul>
Within your AngularJS controller:
$scope.isActive = function (viewLocation) {
var active = (viewLocation === $location.path());
return active;
};
If you are working with Angular router, the RouterLinkActive
directive can be used really elegantly:
<ul class="navbar-nav">
<li class="nav-item"><a class="nav-link" routerLink="home" routerLinkActive="active">Home</a></li>
<li class="nav-item"><a class="nav-link" routerLink="gallery" routerLinkActive="active">Gallery</a></li>
<li class="nav-item"><a class="nav-link" routerLink="pricing" routerLinkActive="active">Prices</a></li>
<li class="nav-item"><a class="nav-link" routerLink="contact" routerLinkActive="active">Contact</a></li>
</ul>
First and foremost, this problem can be solved in a lot of ways. This way might not be the most elegant, but it cerntainly works.
Here is a simple solution you should be able to add to any project. You can just add a "pageKey" or some other property when you configure your route that you can use to key off of. Additionally, you can implement a listener on the $routeChangeSuccess method of the $route object to listen for the successful completion of a route change.
When your handler fires you get the page key, and use that key to locate elements that need to be "ACTIVE" for this page, and you apply the ACTIVE class.
Keep in mind you need a way to make ALL the elements "IN ACTIVE". As you can see i'm using the .pageKey class on my nav items to turn them all off, and I'm using the .pageKey_{PAGEKEY} to individually turn them on. Switching them all to inactive, would be considered a naive approach, potentially you'd get better performance by using the previous route to make only active items inactive, or you could alter the jquery selector to only select active items to be made inactive. Using jquery to select all active items is probably the best solution because it ensures everything is cleaned up for the current route in case of any css bugs that might have been present on the previous route.
Which would mean changing this line of code:
$(".pagekey").toggleClass("active", false);
to this one
$(".active").toggleClass("active", false);
Here is some sample code:
Given a bootstrap navbar of
<div class="navbar navbar-inverse">
<div class="navbar-inner">
<a class="brand" href="#">Title</a>
<ul class="nav">
<li>Home</li>
<li>Page 1 Create</li>
<li>Page 1 Edit</li>
<li>Page 1 Published</li>
</ul>
</div>
</div>
And an angular module and controller like the following:
<script type="text/javascript">
function Ctrl($scope, $http, $routeParams, $location, $route) {
}
angular.module('BookingFormBuilder', []).
config(function ($routeProvider, $locationProvider) {
$routeProvider.
when('/', {
template: 'I\'m on the home page',
controller: Ctrl,
pageKey: 'HOME' }).
when('/page1/create', {
template: 'I\'m on page 1 create',
controller: Ctrl,
pageKey: 'CREATE' }).
when('/page1/edit/:id', {
template: 'I\'m on page 1 edit {id}',
controller: Ctrl, pageKey: 'EDIT' }).
when('/page1/published/:id', {
template: 'I\'m on page 1 publish {id}',
controller: Ctrl, pageKey: 'PUBLISH' }).
otherwise({ redirectTo: '/' });
$locationProvider.hashPrefix("!");
}).run(function ($rootScope, $http, $route) {
$rootScope.$on("$routeChangeSuccess",
function (angularEvent,
currentRoute,
previousRoute) {
var pageKey = currentRoute.pageKey;
$(".pagekey").toggleClass("active", false);
$(".pagekey_" + pageKey).toggleClass("active", true);
});
});
</script>
You can actually use angular-ui-utils' ui-route directive:
<a ui-route ng-href="/">Home</a>
<a ui-route ng-href="/about">About</a>
<a ui-route ng-href="/contact">Contact</a>
or:
Header Controller
/**
* Header controller
*/
angular.module('myApp')
.controller('HeaderCtrl', function ($scope) {
$scope.menuItems = [
{
name: 'Home',
url: '/',
title: 'Go to homepage.'
},
{
name: 'About',
url: '/about',
title: 'Learn about the project.'
},
{
name: 'Contact',
url: '/contact',
title: 'Contact us.'
}
];
});
Index page
<!-- index.html: -->
<div class="header" ng-controller="HeaderCtrl">
<ul class="nav navbar-nav navbar-right">
<li ui-route="{{menuItem.url}}" ng-class="{active: $uiRoute}"
ng-repeat="menuItem in menuItems">
<a ng-href="#{{menuItem.url}}" title="{{menuItem.title}}">
{{menuItem.name}}
</a>
</li>
</ul>
</div>
If you're using ui-utils, you may also be interested in ui-router for managing partial/nested views.
I find all of these answers a bit over complicated for me, sorry. So I have created a small directive that should work on a per navbar basis:
app.directive('activeLink', function () {
return {
link: function (scope, element, attrs) {
element.find('.nav a').on('click', function () {
angular.element(this)
.parent().siblings('.active')
.removeClass('active');
angular.element(this)
.parent()
.addClass('active');
});
}
};
});
Usage:
<ul class="nav navbar-nav navbar-right" active-link>
<li class="nav active">Home</li>
<li class="nav">Foo</li>
<li class="nav">Bar</li>
</ul>
I use ng-class directive with $location to achieve it.
<ul class="nav">
<li data-ng-class="{active: ($location.path() == '/') }">
Carpeta Amarilla
</li>
<li class="dropdown" data-ng-class="{active: ($location.path() == '/auditoria' || $location.path() == '/auditoria/todos') }">
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
Auditoria
<b class="caret"></b>
</a>
<ul class="dropdown-menu pull-right">
<li data-ng-class="{active: ($location.path() == '/auditoria') }">
Por Legajo
</li>
<li data-ng-class="{active: ($location.path() == '/auditoria/todos') }">
General
</li>
</ul>
</li>
</ul>
It requires the navbar to be inside a main Controller with access to $location service like this:
bajasApp.controller('MenuCntl', ['$scope','$route', '$routeParams', '$location',
function MenuCntl($scope, $route, $routeParams, $location) {
$scope.$route = $route;
$scope.$location = $location;
$scope.$routeParams = $routeParams;
}]);
If you use ui-router, the following example should satisfy your needs based on #DanPantry's comment on the accepted answer without adding any controller-side code:
<div class="collapse navbar-collapse" ng-controller="HeaderController">
<ul class="nav navbar-nav">
<li ui-sref-active="active"><a ui-sref="app.home()" href="/">Home</a></li>
<li ui-sref-active="active"><a ui-sref="app.dogs()" href="/dogs">Dogs</a></li>
<li ui-sref-active="active"><a ui-sref="app.cats()" href="/cats">Cats</a></li>
</ul>
</div>
<div ng-view></div>
You can check the docs for more info on it.
You can achieve this with a conditional in an angular expression, such as:
link
That being said, I do find an angular directive to be the more "proper" way of doing it, even though outsourcing a lot of this mini-logic can somewhat pollute your code base.
I use conditionals for GUI styling every once in a while during development, because it's a little quicker than creating directives. I couldn't tell you an instance though in which they actually remained in the code base for long. In the end I either turn it into a directive or find a better way to solve the problem.
If you would rather not use AngularStrap then this directive should do the trick!. This is a modification of https://stackoverflow.com/a/16231859/910764.
JavaScript
angular.module('myApp').directive('bsNavbar', ['$location', function ($location) {
return {
restrict: 'A',
link: function postLink(scope, element) {
scope.$watch(function () {
return $location.path();
}, function (path) {
angular.forEach(element.children(), (function (li) {
var $li = angular.element(li),
regex = new RegExp('^' + $li.attr('data-match-route') + '$', 'i'),
isActive = regex.test(path);
$li.toggleClass('active', isActive);
}));
});
}
};
}]);
HTML
<ul class="nav navbar-nav" bs-navbar>
<li data-match-route="/home">Home</li>
<li data-match-route="/about">About</li>
</ul>
Note: The above HTML classes assume you are using Bootstrap 3.x
Heres my take on it. A little of a combination of answers found on this post. I had a slightly different case, so my solution involves separating the menu into its own template to be used within the Directive Definition Ojbect then add my navbar to the page I needed it on. Basically, I had a login page that I didnt want to include my menu on, so I used ngInclude and insert this directive when logged in:
DIRECTIVE:
module.directive('compModal', function(){
return {
restrict: 'E',
replace: true,
transclude: true,
scope: true,
templateUrl: 'templates/menu.html',
controller: function($scope, $element, $location){
$scope.isActive = function(viewLocation){
var active = false;
if(viewLocation === $location.path()){
active = true;
}
return active;
}
}
}
});
DIRECTIVE TEMPLATE (templates/menu.html)
<ul class="nav navbar-nav">
<li ng-class="{ active: isActive('/View1') }">View 1</li>
<li ng-class="{ active: isActive('/View2') }">View 2</li>
<li ng-class="{ active: isActive('/View3') }">View 3</li>
</ul>
HTML WHICH INCLUDES THE DIRECTIVE
<comp-navbar/>
Hope this helps
Extending myl answer, I needed this to handle an structure like this.
-index
-events<-active
---event-list
---event-edit
---event-map <-clicked
-places
---place-list
---place-edit
---place-map
so instead of matching, I had to use indexOf, in order to validate children links which are formatted to match the condition. So for 'events':
<li ng-class="{ active: isActive('/event')}" class="divider-vertical dropdown">
function NavController($scope, $location) {
$scope.isActive = function (viewLocation) {
var s=false;
if($location.path().indexOf(viewLocation) != -1){
s = true;
}
return s;
};}
This is a simple solution
<ul class="nav navbar-nav navbar-right navbar-default menu">
<li ng-class="menuIndice == 1 ? 'active':''">
<a ng-click="menuIndice = 1" href="#/item1">item1</a>
</li>
<li ng-class="menuIndice == 2 ? 'active':''">
<a ng-click="menuIndice = 2" href="#/item2">item2</a>
</li>
<li ng-class="menuIndice == 3 ? 'active':''">
<a ng-click="menuIndice = 3" href="#/item3">item3</a>
</li>
</ul>
Use an object as a switch variable.
You can do this inline quite simply with:
<ul class="nav navbar-nav">
<li ng-class="{'active':switch.linkOne}" ng-click="switch = {linkOne: true}">Link One</li>
<li ng-class="{'active':switch.linkTwo}" ng-click="switch = {link-two: true}">Link Two</li>
</ul>
Each time you click on a link the switch object is replaced by a new object where only the correct switch object property is true. The undefined properties will evaluate as false and so the elements which depend on them will not have the active class assigned.
In conjunction with #Olivier's AngularStrap answer, I also implemented kevinknelson's answer from: https://github.com/twbs/bootstrap/issues/9013.
Natively, the Bootstrap3 navbar was not designed for a single-page (eg Angular) application and thus the menu when on a small screen was not collapsing upon click.
JavaScript
/**
* Main AngularJS Web Application
*/
var app = angular.module('yourWebApp', [
'ngRoute'
]);
/**
* Setup Main Menu
*/
app.controller('MainNavCtrl', [ '$scope', '$location', function ( $scope, $location) {
$scope.menuItems = [
{
name: 'Home',
url: '/home',
title: 'Welcome to our Website'
},
{
name: 'ABOUT',
url: '/about',
title: 'Know about our work culture'
},
{
name: 'CONTACT',
url: '/contact',
title: 'Get in touch with us'
}
];
$scope.isActive = function (viewLocation) {
return viewLocation === $location.path();
};
}]);
HTML
<div class="navbar-collapse collapse" ng-controller="MainNavCtrl">
<ul id="add-magic-line" class="nav navbar-nav navbar-right">
<li data-ng-class="{current_page_item: isActive('{{ menuItem.url }}')}" data-ng-repeat="menuItem in menuItems">
<a data-ng-href="#{{menuItem.url}}" title="{{menuItem.title}}">
{{menuItem.name}}
</a>
</li>
</ul>
</div>
Thanks to #Pylinux. I have used his technique and also modified it to support "one" level of drop down menu (sub ul/li), as that is what I needed. See it in action in the fiddle link below.
Updated Fiddle based on pylinux's answer - http://jsfiddle.net/abhatia/en4qxw6g/
I made the following three changes, in order to support one level drop down menu:
1. Added a class value of dd (dropdown) for "a" element under li which needs to have sub ul list.
<li><a class="dd">This link points to #/fun5</a>
<ul>
<li>This link points to #/fun6
</li>
<li>This link points to #/fun7
</li>
<li>This link points to #/fun8
</li>
<li>This link points to #/fun9
</li>
</ul>
</li>
2. Updated Javascript to add the following new logic.
if(angular.element(li).parent().parent().children('a').hasClass("dd"))
{angular.element(li).parent().parent().children('a.dd').addClass('active');}
3. Updated CSS to the add the following:
a.active {background-color:red;}
Hopefully this will be helpful to someone looking to implement single level dropdown menu.
You can also use this active-link directive https://stackoverflow.com/a/23138152/1387163
Parent li will get active class when location matches /url:
<li>
<a href="#!/url" active-link active-link-parent>
</li>
I suggest using a directive on a link.
Here is the fiddle.
But its not perfect yet. Watch out for the hashbangs ;)
Here is the javascript for directive:
angular.module('link', []).
directive('activeLink', ['$location', function(location) {
return {
restrict: 'A',
link: function(scope, element, attrs, controller) {
var clazz = attrs.activeLink;
var path = attrs.href;
path = path.substring(1); //hack because path does not return including hashbang
scope.location = location;
scope.$watch('location.path()', function(newPath) {
if (path === newPath) {
element.addClass(clazz);
} else {
element.removeClass(clazz);
}
});
}
};
}]);
and here is how it would be used in html:
<div ng-app="link">
One
One
home
</div>
afterwards styling with css:
.active{ color:red; }
Just to add my two cents in the debate I have made a pure angular module (no jquery), and it will also work with hash urls containing data. (i.g. #/this/is/path?this=is&some=data)
You just add the module as a dependency and auto-active to one of the ancestors of the menu. Like this:
<ul auto-active>
<li>main</li>
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
And the module look like this:
(function () {
angular.module('autoActive', [])
.directive('autoActive', ['$location', function ($location) {
return {
restrict: 'A',
scope: false,
link: function (scope, element) {
function setActive() {
var path = $location.path();
if (path) {
angular.forEach(element.find('li'), function (li) {
var anchor = li.querySelector('a');
if (anchor.href.match('#' + path + '(?=\\?|$)')) {
angular.element(li).addClass('active');
} else {
angular.element(li).removeClass('active');
}
});
}
}
setActive();
scope.$on('$locationChangeSuccess', setActive);
}
}
}]);
}());
* (You can of course just use the directive part)
** It's also worth noticing that this doesn't work for empty hashes (i.g. example.com/# or just example.com) it needs to have at least example.com/#/ or just example.com#/. But this happens automatically with ngResource and the like.
Here is the fiddle: http://jsfiddle.net/gy2an/8/
Here is the github: https://github.com/Karl-Gustav/autoActive
Here is my original answer: https://stackoverflow.com/a/22282124/1465640
This did the trick for me:
var domain = '{{ DOMAIN }}'; // www.example.com or dev.example.com
var domain_index = window.location.href.indexOf(domain);
var long_app_name = window.location.href.slice(domain_index+domain.length+1);
// this turns http://www.example.com/whatever/whatever to whatever/whatever
app_name = long_app_name.slice(0, long_app_name.indexOf('/'));
//now you are left off with just the first whatever which is usually your app name
then you use jquery(works with angular too) to add class active
$('nav a[href*="' + app_name+'"]').closest('li').addClass('active');
and of course the css:
.active{background:red;}
this works if you have your html like this:
<ul><li>ee</li><li>dd</li></ul>
this will atumatically add class active using the page url and color your background to red if your in www.somesite.com/ee thaen ee is the 'app' and it will be active
This is long answered but I thought I'd share my way:
.run(function($rootScope, $state){
$rootScope.$state = $state;
});
Template:
<ul class="nav navbar-nav">
<li ng-class="{ active: $state.contains('View1') }">View 1</li>
<li ng-class="{ active: $state.contains('View2') }">View 2</li>
<li ng-class="{ active: $state.contains('View3') }">View 3</li>
</ul>
For those using ui-router:
<ul class="nav navbar-nav">
<li ui-sref-active="active">View 1</li>
<li ui-sref-active="active">View 2</li>
<li ui-sref-active="active">View 3</li>
</ul>
For exact match (eg nested states?) use $state.name === 'full/path/to/state' or ui-sref-active-eq="active"
Here's another solution for anyone who might be interested. The advantage of this is it has fewer dependencies. Heck, it works without a web server too. So it's completely client-side.
HTML:
<nav class="navbar navbar-inverse" ng-controller="topNavBarCtrl"">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="#"><span class="glyphicon glyphicon-home" aria-hidden="true"></span></a>
</div>
<ul class="nav navbar-nav">
<li ng-click="selectTab()" ng-class="getTabClass()">Home</li>
<li ng-repeat="tab in tabs" ng-click="selectTab(tab)" ng-class="getTabClass(tab)">{{ tab }}</li>
</ul>
</div>
Explanation:
Here we are generating the links dynamically from an angularjs model using the directive ng-repeat. Magic happens with the methods selectTab() and getTabClass() defined in the controller for this navbar presented below.
Controller:
angular.module("app.NavigationControllersModule", [])
// Constant named 'activeTab' holding the value 'active'. We will use this to set the class name of the <li> element that is selected.
.constant("activeTab", "active")
.controller("topNavBarCtrl", function($scope, activeTab){
// Model used for the ng-repeat directive in the template.
$scope.tabs = ["Page 1", "Page 2", "Page 3"];
var selectedTab = null;
// Sets the selectedTab.
$scope.selectTab = function(newTab){
selectedTab = newTab;
};
// Sets class of the selectedTab to 'active'.
$scope.getTabClass = function(tab){
return selectedTab == tab ? activeTab : "";
};
});
Explanation:
selectTab() method is called using ng-click directive. So when the link is clicked, the variable selectedTab is set to the name of this link. In the HTML you can see that this method is called without any argument for Home tab so that it will be highlighted when the page loads.
The getTabClass() method is called via ng-class directive in the HTML. This method checks if the tab it is in is the same as the value of the selectedTab variable. If true, it returns "active" else returns "" which is applied as the class name by ng-class directive. Then whatever css you have applied to class active will be applied to the selected tab.
Just you'll have to add the required active-class with required color code.
Ex: ng-class="{'active': currentNavSelected}" ng-click="setNav"

Categories