How to "instantiate" angular service provider as early as possible? - javascript

I have successfully implemented most of what is described here.
https://github.com/colthreepv/angular-login-example
My issue now is that the stateChangeStart event-handler in my login service does not seem to be registered yet when my grandfather resolve executes. The event handler never fires when I initially load the page, it only fires when I change state again after loading the first state. This makes me thing that my resolve is being executed before the stateChangeStart handler in my login-service has been registered. What can I do to make sure the event handlers has been registered when my root state resolve executes?
The global app route and the resolve:
.state('app', {
abstract: true,
templateUrl: 'vassets/partials/partial-nav.html',
resolve: {
'login': ['loginService','$q', '$http', function (loginService, $q, $http) {
loginService.pendingStateChange;
var roleDefined = $q.defer();
/**
* In case there is a pendingStateChange means the user requested a $state,
* but we don't know yet user's userRole.
*
* Calling resolvePendingState makes the loginService retrieve his userRole remotely.
*/
if (loginService.pendingStateChange) {
return loginService.resolvePendingState($http.get('/session'));
} else {
roleDefined.resolve();
}
return roleDefined.promise;
}]
}
})
My login-service looks like this (the handlers are setup by the managePermissions() call at the bottom of the service):
/*global define */
'use strict';
define(['angular'], function(angular) {
/* Services */
angular.module('myApp.services', [])
.provider('loginService', function () {
var userToken = localStorage.getItem('userToken'),
errorState = 'app.error',
logoutState = 'app.home';
this.$get = function ($rootScope, $http, $q, $state, AUTH_EVENTS) {
/**
* Low-level, private functions.
*/
var managePermissions = function () {
// Register routing function.
$rootScope.$on('$stateChangeStart', function (event, to, toParams, from, fromParams) {
if (wrappedService.userRole === null) {
wrappedService.doneLoading = false;
wrappedService.pendingStateChange = {
to: to,
toParams: toParams
};
return;
}
if (to.accessLevel === undefined || to.accessLevel.bitMask & wrappedService.userRole.bitMask) {
angular.noop(); // requested state can be transitioned to.
} else {
event.preventDefault();
$rootScope.$emit('$statePermissionError');
$state.go(errorState, { error: 'unauthorized' }, { location: false, inherit: false });
}
});
};
/**
* High level, public methods
*/
var wrappedService = {
loginHandler: function (data, status, headers, config) {
// update user
angular.extend(wrappedService.user, data.user);
wrappedService.isLogged = true;
wrappedService.userRole = data.user.roles[0].roleName;
$rootScope.$broadcast(AUTH_EVENTS.loginSuccess);
$rootScope.currentUser = data.user;
return data.user;
},
loginUser: function (httpPromise) {
httpPromise.success(this.loginHandler);
},
resolvePendingState: function (httpPromise) {
var checkUser = $q.defer(),
self = this,
pendingState = self.pendingStateChange;
httpPromise.success(
function success(httpObj) {
if (!httpObj.user) {
getLoginData();
}
else {
self.loginHandler(httpObj);
}
}
);
httpPromise.then(
function success(httpObj) {
self.doneLoading = true;
if (pendingState.to.accessLevel === undefined || pendingState.to.accessLevel.bitMask & self.userRole.bitMask) {
checkUser.resolve();
} else {
checkUser.reject('unauthorized');
}
},
function reject(httpObj) {
checkUser.reject(httpObj.status.toString());
}
);
self.pendingStateChange = null;
return checkUser.promise;
},
/**
* Public properties
*/
userRole: null,
user: {},
isLogged: null,
pendingStateChange: null,
doneLoading: null
};
managePermissions();
return wrappedService;
};
})
});

Related

Implementing loading using httpInterceptor and AngularJS

i try to make loading icon appears in all website while requests in all components that requires time in loading i write this code but it didn't work !
this is the directive and controller
(function() {
'use strict';
angular
.module('xreview')
.directive('loading', loading);
/** #ngInject */
function loading() {
var directive = {
restrict: 'E',
templateUrl: 'app/components/directives/loading/loading.html',
scope: {
},
controller: loadingController,
controllerAs: 'scope',
bindToController: true
};
return directive;
/** #ngInject */
function loadingController($rootScope , $httpInterceptor ) {
return function ($scope, element, attrs) {
$scope.$on("loader_show", function () {
return element.show();
});
return $scope.$on("loader_hide", function () {
return element.hide();
});
};
}
}
})();
this is the interceptor factory
(function() {
'use strict';
angular
.module('xreview')
.factory('httpInterceptor', httpInterceptor);
/** #ngInject */
function httpInterceptor($q, $rootScope, $log) {
var numLoadings = 0;
return {
request: function (config) {
numLoadings++;
// Show loader
$rootScope.$broadcast("loader_show");
return config || $q.when(config)
},
response: function (response) {
if ((--numLoadings) === 0) {
// Hide loader
$rootScope.$broadcast("loader_hide");
}
return response || $q.when(response);
},
responseError: function (response) {
if (!(--numLoadings)) {
// Hide loader
$rootScope.$broadcast("loader_hide");
}
return $q.reject(response);
}
};
}
})();
and i injected this in config
$httpProvider.interceptors.push('httpInterceptor');
for example this is a service of on component of the components
vm.postAllComment = function(file){
vm.commentig = true;
var modal = {
comment: vm.allCommentText
};
if (file.file_name != 'Post') {
modal.post_file = file.id;
}
userService.one('reviews', id).post('comments', modal).then(function(result){
vm.commentig = false;
vm.allComments.push.apply(vm.allComments, [{
user: result.user,
content: result.comment,
id: result.id
}]);
vm.allCommentText = '';
vm.post.comments_count ++;
}, function(error){
// error post comment
if (error.status == 403)
userService.userUnauthenticatedHandler();
vm.commentig = false;
});
};
this is the html
<div id="loaderDiv" loading>
<img src="./assets/image/spinner.gif" class="ajax-loader"/>
</div>

angular get and set atributes via service

i can't find a solution to this, basicly everytime i do a login, i want to store the user that i get from the node end point in the service, after that in my main Controller i should get the name of the user, but that never happen, dunno why
here is the code:
app.controller('MainCtrl', function ($scope, $state,$location,$http,user) {
$scope.user = {
nome: user.getProperty()
};
$scope.showRegister = function () {
$state.go('register');
}
$scope.showLogin = function () {
$state.go('login');
}
});
app.controller('loginController', function ($scope, $http, $state,user) {
$scope.login = function () {
var data = {};
data.password = $scope.loja.password;
data.email = $scope.loja.email;
$http.post('http://localhost:8080/login/',data)
.success(function (data) {
console.log(data);
user.setProperty(data.nome);
$state.go('home');
})
.error(function (statusText) {
console.log("failed");
});
}
});
user service
app.service('user', function () {
var property = {};
return {
getProperty: function () {
return property.nome;
},
setProperty: function (value) {
property.nome = value;
}
};
});
You could just watch your service for changes by adding this code to your MainCtrl:
$scope.$watch(function () { return user.getProperty();}, updateProp, true);
function updateProp(newValue, oldValue) {
$scope.user = {
nome: newValue
};
}
updateProp gets executed everytime the value of user.getProperty() changes.
Your main issue is with your MainCtrl . In the initial execution of MainCtrl there is no value set into your service so its get blank. MainCtrl executes before setting the value in the service.
$scope.user = {
nome: user.getProperty()
};
this code should be executed after setting the value in the service but it executes in the initialization of controller.
You can get the reference from the fiddle below.
http://jsfiddle.net/ADukg/9799/

Is it possible to resolve some data before $stateChangeStart is fired?

I'm trying to build Role-Permissions system that I want to initialize on root state resolve:
$stateProvider
.state('common', {
resolve:{
user: function(AclService, UserService) {
UserService.getCurrent().then((currentUser) => {
AclService.initialize(currentUser);
});
}
}
})
and check permissions each time on $stateChangeStart:
$rootScope.$on('$stateChangeStart', ($event, toState) => AclService.interceptStateChange($event, toState));
but I faced a problem that first $stateChangeStart was fired before resolve, so permissions were not initialized yet.
What would you recommend in such situation?
You could do that in your app's run function. Here is a trimmed down version of how I load auth data up front.
(function() {
"use strict";
angular
.module("myModule", [ //dependencies here...]);
angular
.module("myModule")
.run(run);
run.$inject = ["$rootScope", "$state", "authService"];
function run($rootScope, $state, authService) {
authService.fillAuthData(); //front load auth stuff here...
$rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState, fromParams) {
var isPublic = (toState.data && toState.data.isPublic && toState.data.isPublic === true);
var requiredRole = (toState.data && toState.data.requiredRole) ? toState.data.requiredRole : null;
var authorized = isPublic || authService.isUserInRole(requiredRole);
if (authService.authentication.isAuth || isPublic) {
//if the user doesn't have the requisite permission to view the page, redirect them to an unauthorized page
if (!authorized) {
event.preventDefault();
$state.go("unauthorized");
return;
}
} else {
event.preventDefault();
$state.go("login");
return;
}
});
}
})();
A state definition may look like this:
.state("someState", {
url: "/someState",
templateUrl: "my/folder/file.html",
data: {
pageTitle: "Some Page",
isPublic: false,
requiredRole: "Admin"
}
})
You shouldn't do some auth logic in state resolves. Better approach is to set listener for $stateChangeStart event in angular.run function:
angular.module('yourModule', [])
.run(['$rootScope', 'principal', '$state', function ($rootScope, principal, $state) {
var firstOpen = true;
$rootScope.$on('$stateChangeStart', function(event, toState, toParams) {
if (!principal.isAuthenticated() && firstOpen) {
firstOpen = false;
event.preventDefault();
principal.checkAuthentication().then(function() {
$state.go(toState, toParams);
});
} else if (principal.isAuthenticated() && toState.name === 'login') {
event.preventDefault();
// Do some stuff here, for example, redirect to main page
}
});
}
]);

Angular UI Router not resolving one service before another

I am trying to create a tag layout filled with categories, but I am not getting my Authentication because I am trying to resolve that service in my Router.
this is my Router code
(function () {
'use strict';
angular
.module('learningApp')
.config(sslRouter);
// Minification safe dependency Injection
sslRouter.$inject = ['$stateProvider'];
function sslRouter ($stateProvider) {
// SSL Route Definition
$stateProvider.state('ssl', {
parent: 'policy',
url: '/ssl',
data: {
roles: ['USER']
},
views: {
'policyConfig': {
templateUrl: 'components/configuration/service/policy/ssl/ssl.tpl.html',
controller: 'SSL'
}
},
resolve: {
'sslServiceData': function(sslService) {
return sslService.promise;
}
}
});
}
}());
This is my Service
(function() {
'use strict';
angular
.module('learningApp')
.factory('sslService', sslResource);
sslResource.$inject = ['Principal', '$resource', 'BASE_URL', 'exDomainService'];
function sslResource (Principal, $resource, BASE_URL, exDomainService) {
debugger;
var res = $resource(BASE_URL + '/api/companies/' + Principal.company() + '/sconfig/ssl/sslConfiguration', {}, {
query: {
method: 'GET',
isArray: false
},
update: {
method: 'PUT'
}
});
var data = {};
var servicePromise = _initService();
servicePromise.$promise.then(function (d) {
data = d;
if (!data.excludedCategories) {
data.excludedCategories = [];
}
if (!data.excludedDomains) {
data.excludedDomains = [];
}
exDomainService.tableData = getExcludedDomains();
});
function _initService () {
return res.query();
}
return {
promise: servicePromise,
rest: res
}
}
}());
This is my controller
(function() {
'use strict';
angular
.module('learningApp')
.controller('SSL', SSLController);
SSLController.$inject = ['$scope', 'sslService', 'preDefinedCategoryService', '$timeout', 'exDialog', 'exDomainService'];
function SSLController ($scope, sslService, preDefinedCategoryService, $timeout, exDialog, exDomainService) {
var vm = $scope;
/**
* #desc Flags for different type checks
* Booleans and Categories
*/
vm.flags = {
// By default true
enableInspectSSLTraffic: sslService.getSSlInspectionFlag(),
allowUntrustedCertificates: sslService.getUntrustedCertificatesFlag(),
allowHostnameMismatch: sslService.getHostnameMismatchFlag(),
selectedCategory: undefined,
initializing: true
};
vm.excludedCategories = sslService.getExcludedCategories();
vm.predefinedCategories = preDefinedCategoryService.rest.query();
vm.predefinedCategories.$promise.then(function() {
vm.categories = _processedCategories(vm.predefinedCategories, vm.excludedCategories);
});
}
}());
So basically problem is, I am getting Principal.Identity as undefined, but if I remove resolution from Router, I got identity but then I lose my data coming from service. I want my service to be loaded completely before its Controller, and I want my principal service to be loaded before service.
for Reference, This is my Principal Class
'use strict';
angular.module('learningApp')
.service('Principal',['$q', 'Account', 'localStorageService', function Principal($q, Account, localStorageService) {
var _identity,
_authenticated = false;
return {
isIdentityResolved: function () {
return angular.isDefined(_identity);
},
isAuthenticated: function () {
return _authenticated;
},
isInRole: function (role) {
if (!_authenticated || !_identity || !_identity.roles) {
return false;
}
return _identity.roles.indexOf(role) !== -1;
},
isInAnyRole: function (roles) {
if (!_authenticated || !_identity.roles) {
return false;
}
for (var i = 0; i < roles.length; i++) {
if (this.isInRole(roles[i])) {
return true;
}
}
return false;
},
company: function () {
debugger;
if (_identity) return _identity.companyId;
},
authenticate: function (identity) {
_identity = identity;
_authenticated = identity !== null;
},
identity: function (force) {
var deferred = $q.defer();
if (force === true) {
_identity = undefined;
}
// check and see if we have retrieved the identity data from the server.
// if we have, reuse it by immediately resolving
if (angular.isDefined(_identity)) {
deferred.resolve(_identity);
return deferred.promise;
}
// rather than retrieving from server, use cookie or whatever method
var cookieFound = UTIL.cookie("token");
if (cookieFound) {
var response = JSON.parse(JSON.parse(cookieFound));
var expiredAt = new Date();
expiredAt.setSeconds(expiredAt.getSeconds() + response.expires_in);
response.expires_at = expiredAt.getTime();
localStorageService.set('token', response);
}
// retrieve the identity data from the server, update the identity object, and then resolve.
Account.get().$promise
.then(function (account) {
account.data.roles = ["ADMIN", 'USER'];
account.data.langKey = "en";
_identity = account.data;
_authenticated = true;
deferred.resolve(_identity);
})
.catch(function() {
_identity = null;
_authenticated = false;
deferred.resolve(_identity);
});
return deferred.promise;
}
};
}]);

How to update html header after login in an angular/node.js application?

I am trying to get a header to update after login. I have used both $on and $watch in this effort to no avail. When I refresh it works correctly. Code is as follows below.
header.html (missing excess nav bar code for simplicity)
<li><a ng-href="#/login" ng-hide="showMenu">Login</a></li>
<li><a ng-href="#/signup" ng-hide="showMenu">Signup</a></li>
<li>Logout</li>
app.js
$stateProvider
.state('app', {
url: '',
views: {
'header': {
templateUrl: 'views/partials/_header.html',
controller: 'HeaderCtrl'
}
}
})
header.js (The broadcast fires correctly as demonstrated by the console.logs)
angular.module('urbinsight')
.controller('HeaderCtrl', function ($scope, $rootScope, $state, $location, UserAuthFactory, AuthFactory) {
$scope.logout = function () {
UserAuthFactory.logout();
$rootScope.$broadcast('loginStateChange');
$location.path('/');
};
$scope.showMenu = AuthFactory.loggedStatus();
$rootScope.$on('loginStateChange', function(){
console.log($scope.showMenu)
$scope.showMenu = AuthFactory.loggedStatus();
console.log($scope.showMenu)
})
})
authService
angular.module('urbinsight.services')
.factory('AuthFactory', function ($window) {
var isLogged = false;
return {
check: function() {
if ($window.sessionStorage.token && $window.sessionStorage.user) {
isLogged = true;
} else {
isLogged = false;
delete this.user;
}
},
loggedStatus: function() {
return isLogged;
},
changeLoggedStatus: function() {
isLogged = !(isLogged);
}
};
})
login function + broadcast
login.submit = function () {
var username = user.username,
password = user.password;
if (username !== undefined && password !== undefined) {
UserAuthFactory.login(username, password).success(function(data) {
$rootScope.showMenu = true
// AuthFactory.isLogged = true;
AuthFactory.changeLoggedStatus();
AuthFactory.user = data.user.username;
AuthFactory.userRole = data.user.role;
$rootScope.$broadcast('loginStateChange');
$window.sessionStorage.token = data.token;
$window.sessionStorage.user = data.user.username;
$window.sessionStorage.userRole = data.user.role;
$location.path('/');
}).error(function(status) {
$window.alert('Oops something went wrong!');
});
} else {
$window.alert('Invalid credentials');
}
};
Please tell me what I am doing wrong.
You have set the "$rootScope.showMenu = true" in your login controller. But in your header controller you also have "$scope.showMenu = AuthFactory.loggedStatus();"
So i would remove this line from your header controller
$scope.showMenu = AuthFactory.loggedStatus();
Since you want your header html to react to $rootscope showMenu variable directly

Categories

Resources