Ui-Router navbar options not refreshing after login/logout - angularjs

I've defined a navbar using angular and ui-bootstrap. There are several nav options which are rendered based on a login state:
If a user or admin is logged in, there should be a Logout option in the nav bar.
If an admin is logged in, the Admin option shows in the nav bar
If a user is logged in, no Admin appears in the navbar, instead a Profile link shows.
I'm using ui-router in this project, and according to the documentation, a parent state is never refreshed when a child state is, unless {refresh : true} is included in the state call. However, nothing refers to refreshing the state of one ui-view based on state of another.
The main index page of the app has two ui-view elements defined: one for nav, and the other for the application content:
<body ng-app="app">
<div ui-view="nav"></div>
<div class="container-fluid">
<div ui-view="appContent"></div>
</div>
</body>
What is occurring is when I click on a Logout nav option, I successfully log out but the nav options do not change unless I refresh the page.
My navbar template:
<div class="navbar-collapse collapse" collapse="navbarCollapsed">
<ul class="nav navbar-nav">
<li>
<a ui-sref="home">Home</a>
</li>
<li ng-if="!userState.isLoggedIn()">
<a ui-sref="login">Login</a>
</li>
<li ng-if="userState.isLoggedIn() && !userState.isAdmin()">
<a ui-sref="profile">Profile</a>
</li>
<li ng-if="userState.isAdmin()">
<a ui-sref="admin">Admin</a>
</li>
<li ng-if="userState.isLoggedIn()">
Logout
</li>
</ul>
</div>
and controller:
angular.module('navigation')
.controller('NavCtrl', function($rootScope, $scope, $state, $log, $sce, authenticationService, userService) {
"use strict";
var user = authenticationService.getUser();
$scope.userState = {
isAdmin : function() {
return user !== null && user.role === 'ADMIN';
},
isLoggedIn : function() {
return user !== null;
}
};
$scope.logoutUser = function() {
userService.clearLocalState();
$state.go('home', {}, {reload : true});
};
});
With the {reload : true} param option, I see a flicker in the nav element on logout, but the option visibility does not change. The only solution that works is a manual page refresh and I'd like to avoid that if at all possible.

Well, you have tovreload content of your navbar view. It's possible without the whole page refreshing. Use $state object. It has reload and other methods for manual manipulating of your views

Navbar template:
<div class="navbar-collapse collapse" collapse="navbarCollapsed">
<ul class="nav navbar-nav">
<li>
<a ui-sref="home">Home</a>
</li>
<li ng-if="!user">
<a ui-sref="login">Login</a>
</li>
<li ng-if="user && user.role !== 'ADMIN'">
<a ui-sref="profile">Profile</a>
</li>
<li ng-if="user.role === 'ADMIN'">
<a ui-sref="admin">Admin</a>
</li>
<li ng-if="user !== null">
Logout
</li>
</ul>
</div>
and controller:
angular.module('navigation')
.controller('NavCtrl', function($rootScope, $scope, $state, $log, $sce, authenticationService, userService) {
"use strict";
$scope.user = authenticationService.getUser();
$scope.logoutUser = function() {
userService.clearLocalState();
$scope.user = authenticationService.getUser();
//$state.go('home', {}, {reload : true});
};
});
It is a dirty "solution" but maybe a solution if you do not want to use $location reload.

Related

I am using Angular 1 and I am trying to ng-hide my login button once a user is logged in?

I know I am suppose to do and ng-hide="something", but I don't where to go from there. Not sure if I have to also write another function in my logoutController or mainController.
<ul class="right hide-on-med-and-down">
<li><a ui-sref="login">Login</a></li>
<li><a ui-sref="logout" ng-click='logoutCtrl.logout()'>Logout</a></li>
</ul>
</div>
nav.html
function logoutController($state, AuthService) {
var vm = this
vm.logout = function () {
// call logout from service
AuthService.logout()
.then(function () {
$state.go('login')
})
}
}
app.js
It depend the way you're login works but you can do
<ul class="right hide-on-med-and-down">
<li><a ng-show="inNotLogged"ui-sref="login">Login</a></li>
<li><a ui-sref="logout" ng-click='logoutCtrl.logout()'>Logout</a></li>
</ul>
</div>
and in your app.js .run set $rootScope.isNotLogged = true;then in your login method:
$rootScope.isNotLogged = false;
$scope.isNotLogged = $rootScope.isNotLogged;
so in tour lgout you will set
$scope.isNotLogged = true;
$rootScope.isNotLogged = $scope.isNotLogged;

AngularJS sidebar link to stay active when clicked

I am creating a sidebar based off of this one
http://plnkr.co/edit/xzcjStdvmkI2rpfMzLjI?p=preview found from this tutorial
However I want to keep the selection active once the link is clicked. I've tried adding data-toggle, but that seems to work for nav-pills instead of navbar. I also found an is-active-nav directive that I've attempted to implement:
angular.module('sidebarmodule')
.directive('isActiveNav', ['$location', function ($location) {
return {
restrict: 'A',
link: function (scope, element) {
scope.location = $location;
scope.$watch('location.path()', function (currentPath) {
if ('/#' + currentPath === element[0].attributes['href'].nodeValue) {
element.parent().addClass('active');
} else {
element.parent().removeClass('active');
}
});
}
};
}]);
That I call in the sidebar template like so:
<li> <a is-active-nav href="#">Link1</a> </li>
Another approach I tried is in my controller adding $scope.isActive = function (viewLocation) { return viewLocation === $location.path(); }; and then adding to the template: <li ng-class="{ active: isActive('/importinvoice')}">First Link</li>
However, nothing I do will keep the sidebar elements highlighted after navigating to that page. I am trying to keep out of jQuery as I want it to be an AngularJS solution only.
I tried most of the solutions from this stackoverflow answer but can't get any of them to work on the plunker.
You can create a tab property in $scope, assign it a tab identifier and then use ng-class="{'active': tab==='tab1'}"
use this :
<div class="sidebar" sidebar-directive="state" ng-init="item=-1">
Navigation
<ul class="navigation">
<li class="navigation-items"> Link1
</li>
<li class="navigation-items"> Link2
</li>
<li class="navigation-items"> Link3
</li>
<li class="navigation-items"> Link4
</li>
<li class="navigation-items"> Link4
</li>
</ul>
</div>
see link : http://plnkr.co/edit/xzcjStdvmkI2rpfMzLjI?p=preview

Implementing angularJs factory or service for bootstrap tabs

how to write a service or factory for bootstrap tabs using angularJs.
I placed in one controller but need a common function(repeated code) for different controllers.
<ul class="nav nav-tabs">
<li data-ng-class="{active:tab===0}">
<a href ng-click="changeTab(0)"> <i class="fa fa-list"></i></a>
</li>
<li data-ng-class="{active:tab===1}">
<a href ng-click="changeTab(1)"><i class="fa fa-user"></i></a>
</li>
</ul>
<div data-ng-class="{activeTab:tab===0}" ng-show="isActiveTab(0)">
tab1
</div>
<div data-ng-class="{activeTab:tab===1}" ng-show="isActiveTab(1)">
tab2
</div>
controller
$scope.tab = 0;
$scope.changeTab = function(newTab){
$scope.tab = newTab;
};
$scope.isActiveTab = function(tab){
return $scope.tab === tab;
};
Managing tabs is a view concern. Rather than implementing a factory, I recommend creating two directives: tabContainer and tab. The tab directive registers itself with the parent tabContainer using the require attribute to access the parent directive's controller API.
Demo
Usage
<tab-container selected="tab2">
<tab name="tab1">tab1</tab>
<tab name="tab2">tab2</tab>
</tab-container>
Parent Directive
The parent directive publishes the following controller API that the child tab directives will access:
tabContainer controller
// registers this tab with the parent tabContainer
this.register = function(element) {
$scope.tabs.push(element);
}
// returns the selected tab object whose
// name property indicates which tab is active
this.getSelected = function() {
return $scope.selectedTab;
}
Child Directive
The tab directive is able to access the parent controller by requiring the parent directive in its directive definition, and accessing the parent directive's controller as the 4th argument to the tab directive's link function:
tab directive definition
scope: true,
require: '^tabContainer',
link: function(scope, element, attr, tabContainer) {
// set the tab so that it is visible in the tab directive's scope.
scope.tab = { name: attr.name, element:element};
scope.selectedTab = tabContainer.getSelected();
tabContainer.register(scope.tab);
}
The scope is set to true so that each tab will create its own child scope and not interfere with the scope of other tabs.
Template Files
For example purposes, the directive templates are embedded in the HTML:
<script type="text/ng-template" id="tabContainer.html">
<ul class="nav nav-tabs">
<li ng-repeat="tab in tabs" data-ng-class="{active:selectedTab.name===tab.name}">
<a href ng-click="changeTab(tab)"> <i class="fa fa-list">{{tab.name}}</i></a>
</li>
</ul>
<ng-transclude></ng-transclude>
</script>
<script type="text/ng-template" id="tab.html">
<div data-ng-class="{activeTab:selectedTab.name===tab.name}" ng-show="selectedTab.name === tab.name">
<ng-transclude></ng-transclude>
</div>
</script>
It is recommended to move these to dedicated HTML files.
Changing the Active Tab
The user is able to change the active tab by clicking the tab link. This is achieved by publishing a $scope function in the parent controller:
$scope.changeTab = function(tab) {
$scope.selectedTab.name = tab.name;
}
Creating a Tabs Module
The beauty of AngularJS and its pluggable modular architecture is that you can extend the AngularJS directive ecosystem, and have the directives work together seamlessly. For example, you could encapsulate the above tabs directive into a tabs module, and even use the ngRepeat directive to bind the tabs.
Demo
Controller
var app = angular.module('app',['tabs']);
app.controller('ctrl', function($scope) {
$scope.tabData = [
{ name: 'tab1', body: 'You selected tab1!'},
{ name: 'tab2', body: 'You selected tab2!'},
{ name: 'tab3', body: 'You selected tab3!'},
{ name: 'tab4', body: 'You selected tab4!'},
];
});
View
<div class="container" ng-controller="ctrl">
<tab-container selected="tab1">
<tab ng-repeat="tab in tabData" name="{{tab.name}}">{{ tab.body }} </tab>
</tab-container>
</div>
Hi I write it with out using any service or factory
see the example
<ul ng-init="tab = 1">
<li ng-class="{active:tab===1}">
<a href ng-click="tab = 1">Tab1</a>
</li>
<li ng-class="{active:tab===2}">
<a href ng-click="tab = 2">Tab2</a>
</li>
<li ng-class="{active:tab===3}">
<a href ng-click="tab = 3">Tab3</a>
</li>
<br><br>
<p ng-show="tab === 1"> Tab1 content </p>
<p ng-show="tab === 2"> Tab2 content</p>
<p ng-show="tab === 3"> Tab3 content</p>
</ul>
Dynamically change it through controller see the working example here
The structure for controllers/services in Angular are explained well here:
https://github.com/johnpapa/angular-styleguide
In app.js, we declare an angular application, give it a name, and any dependencies (ng-route / ng-grid). $http calls should be made using a factory or a service, and the controller should call the service to fetch or post data. From the angular documentation, "services are substitutable objects that are wired together using dependency injection (DI). You can use services to organize and share code across your app."
https://docs.angularjs.org/guide/services
app.js:
var app = angular.module('appname', []);
app.factory('httpResponseErrorInterceptor'...
app.config(['$httpProvider',...
Controller:
angular.module('appname').controller("NameCtrl", ["$scope", "$log", "$window", "$http", "$timeout", "SomeService",
function ($scope, $log, $window, $http, $timeout, TabService) {
//your controller code
$scope.tab = 0;
$scope.changeTab = function(newTab){
$scope.tab = newTab;
};
$scope.isActiveTab = function(tab){
return $scope.tab === tab;
};
}
]);
Service:
angular.module('appname').service("TabService", function () {
//Your code for shared functionality regarding tab service
var currentTab = {};
return {
currentTab : currentTab
}
});

Angular.js, router-ui and Bootstrap: Activate State of Clicked Button

I have this nav.html:
<ul class="nav nav-pills pull-right">
<li ng-class="{ active: $state.includes('home') }"><a ui-sref="home">Home</a></li>
<li ng-class="{ active: $state.includes('about') }"><a ui-sref="about">About</a></li>
<li ng-class="{ active: $state.includes('contact') }"><a ui-sref="contact">Contact</a></li>
</ul>
<h3 class="text-muted">my page title</h3>
</div
This Controller:
.module('app', [
'ui.router'
])
.config(['$urlRouterProvider','$stateProvider', function($urlRouterProvider,$stateProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('about', {
url: '/about',
templateUrl: 'templates/about.html',
controller: 'aboutCtrl'
})
// and so forth
Now when I click a button in the navigation, the ui-router correctly displays tha page and changes the url in the browser adress bar. However Bootstrap does not make the button activated, aka selected or color it grey etc. How to achieve this the angular way?
You must make sure that $state service is defined/injected in your current scope. You may have a navCtrl that handles the rendering logic of your navbar html. Remember to inject that controller in your nav.html:
<ul ng-controller = "navCtrl" class="nav nav-pills pull-right">
Next, write a boolean function inside your navCtrl to check the current state, using $state.includes:
.controller('navCtrl',['$scope','$state','$location',function($scope,$state,$location){
$scope.isState = function(states){
return $state.includes(states);
};
}])
and lastly use this function in your html:
<li ng-class="{active: isState('home') }">
<a ui-sref="home">Home</a>
</li>
Here is the working plnkr.

How can create a dynamic menu depending on the user's rights, in angularjs?

There is two similar questions:
dynamic menu items using AngularJS
How can I show or hide some buttons depend on the user's rights, in angularjs?
I need to create a dynamic menu exactly like in the first similar question exept I cannot hardcode the rights in my page. An adminitrator can create custom roles and chose wich menu item this role can see.
<li class="dropdown"><img role="button" class="dropdown-toggle" data-toggle="dropdown" ng-src="{{avatarUrl}}" />
<ul class="dropdown-menu pull-right" role="menu">
<li ng-show="???"><a ng-click="action01()">Action one</a></li>
<li ng-show="???"><a ng-click="action02()">Action two</a></li>
<li ng-show="???"><a ng-click="action03()">Action tree</a></li>
<li ng-show="???"><a ng-click="action04()">Action four</a></li>
</ul>
</li>
How should I imagine my strategy?
I would create something like a HeaderController an attach to it a function that tells if a given role can do the given action. Presumably you have the ACL stored somewhere so perhaps you can create a service for it. Something like this:
controller('HeaderController', ['$scope', 'Acl', function($scope, Acl) {
$scope.roleCanDo = function(role, action) {
return Acl.roleCanDo(role, action);
}
}])
and your view would be like this:
<li class="dropdown"><img role="button" class="dropdown-toggle" data-toggle="dropdown" ng-src="{{avatarUrl}}" />
<ul class="dropdown-menu pull-right" role="menu">
<li ng-show="roleCanDo(currentUser.role, 'action01')"><a ng-click="action01()">Action one</a></li>
<li ng-show="roleCanDo(currentUser.role, 'action02')"><a ng-click="action02()">Action two</a></li>
<li ng-show="roleCanDo(currentUser.role, 'action03')"><a ng-click="action03()">Action tree</a></li>
<li ng-show="roleCanDo(currentUser.role, 'action04')"><a ng-click="action04()">Action four</a></li>
</ul>
</li>
Obviously the actual code will depend on your current system but you get the idea.
It's very simple to implement Creating dynamic menu from database data using AngularJS
Suppose we have a table in our database for navigation menus like,
Table structure for dynamic menu
Write below code for fetch menus from database
public JsonResult GetSiteMenu()
{
using (MyDatabaseEntities dc = new MyDatabaseEntities())
{
var menu = dc.SiteMenus.ToList();
return new JsonResult { Data = menu, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
}
and here is the angularjs controller
var app = angular.module('MyApp', []);
app.controller('menuController', ['$scope', '$http', function ($scope, $http) {
$scope.SiteMenu = [];
$http.get('/home/GetSiteMenu').then(function (data) {
$scope.SiteMenu = data.data;
}, function (error) {
alert('Error');
})
}])
HTML Code
<div ng-app="MyApp">
<div ng-controller="menuController">
#* Here first of all we will create a ng-template *#
<script type="text/ng-template" id="treeMenu">
{{menu.Name}}
#* We will create submenu only when available *#
<ul ng-if="(SiteMenu | filter:{ParentID : menu.ID}).length > 0">
<li ng-repeat="menu in SiteMenu | filter:{ParentID : menu.ID}" ng-include="'treeMenu'"></li>
</ul>
</script>
<ul class="main-navigation">
#* Here we will load only top level menu *#
<li ng-repeat="menu in SiteMenu | filter:{ParentID : 0}" ng-include="'treeMenu'"></li>
</ul>
</div>
</div>

Resources