Conditionally navigate to state with angular ui-router - javascript

Currently, we have a 'Portfolio' tool in beta. Once a user logs in to the main app, if they have been given access to the beta, they can navigate to the Portfolio tool directly, without any additional login. If not, they should be redirected to a Portfolio login page (state is called portfolio.login) where they can login or contact support/sales etc. Right now I have the check in the resolve block, however $state.go('portfolio.login') seems to fetch the right partials, but doesn't render them on screen or navigate to the appropriate URL.
Code:
angular.module('portfolio.manager').config(function ($logProvider, $stateProvider) {
'use strict';
$stateProvider
.state('portfolio.manager', {
url: '/manager',
resolve: {
CheckLoggedIn: function ($state, loggedIn) {
var _loggedIn = loggedIn.checkUser();
if (!_loggedIn) {
$state.go('portfolio.login');
console.log('not authorized');
}
},
portfolioAuthService: 'portfolioAuthService',
User: function(portfolioAuthService){
return portfolioAuthService.getUser();
},
Portfolios: function (User, portfolioManagerService) {
return portfolioManagerService.getPortfolios();
}
},
views: {
'main#': {
templateUrl: 'app/portfolio/manager/portfolio-manager.html',
controller: 'PortfolioManagerCtrl'
},
'no-portfolios#portfolio.manager': {
templateUrl: 'app/portfolio/manager/partials/no-portfolios.html'
},
'create#portfolio.manager': {
templateUrl: 'app/portfolio/manager/partials/create.html'
}
}
})

I ran in the same problem days ago. Instead of using resolve, I check if the user is logged when state changes, defining run module and listening $stateChangeStart event, then check if the current state required authentication. If so, check if the user is logged in.
angular.module('portfolio.manager').config(function ($logProvider, $stateProvider) {
'use strict';
$stateProvider
.state('portfolio.manager', {
url: '/manager',
resolve: {
portfolioAuthService: 'portfolioAuthService',
User: function(portfolioAuthService){
return portfolioAuthService.getUser();
},
Portfolios: function (User, portfolioManagerService) {
return portfolioManagerService.getPortfolios();
}
},
data: {
requiredAuthentication: true
},
views: {
'main#': {
templateUrl: 'app/portfolio/manager/portfolio-manager.html',
controller: 'PortfolioManagerCtrl'
},
'no-portfolios#portfolio.manager': {
templateUrl: 'app/portfolio/manager/partials/no-portfolios.html'
},
'create#portfolio.manager': {
templateUrl: 'app/portfolio/manager/partials/create.html'
}
}
})
})
.run(run);
run.$inject = ['$rootScope','$state','loggedIn'];
function run($rootScope,$state,loggedIn){
$rootScope.$on('$stateChangeStart',function(e,toState){
if ( !(toState.data) ) return;
if ( !(toState.data.requiredAuthentication) ) return;
var _requiredAuthentication = toState.data.requiredAuthentication;
if (_requiredAuthentication && !loggedIn.checkUser() ){
e.preventDefault();
$state.go('portfolio.login', { notify: false });
console.log('not authorized');
}
return;
});
};

Related

Angular-permission roles doesn't redirect if role condition is false

I'm using angular-permission 5.3.2. Here is my app.run.js
(function() {
'use strict';
angular
.module('myApp')
.run(run);
/** #ngInject */
function run(PermRoleStore, PermPermissionStore, $auth)
{
PermRoleStore
.defineRole('user', function (stateParams) {
if($auth.isAuthenticated()) {
return true; // Is loggedin
}
return false;
});
}})();
And here is how I check for permission for route view2
.state('view2', {
url: '/view2',
templateUrl: 'view2/view2.html',
controller: 'View2Ctrl as vm',
data: {
permissions: {
only: 'user',
redirectTo: 'view1'
}
},
});
The problem is that I'm not redirected to view1, even if I'm not logged in. Does anyone know where's the problem?

Angularjs Login Authentication: Prevent user navigate to other page except login page and registration page

I am developing a web application by using AngularJS framework for my frontend. For my login page, I have to prevent user browse to other page except login page and registration. But the code that what I did now, prevent user navigate to registration page also. The following is my code. How can I solve this problem in order that enable user to browse to login page and registration page only if the user without login.
.run(function ($rootScope, $state, AuthService, AUTH_EVENTS) {
$rootScope.$on('$stateChangeStart', function (event,next, nextParams, fromState) {
if ('data' in next && 'authorizedRoles' in next.data) {
var authorizedRoles = next.data.authorizedRoles;
if (!AuthService.isAuthorized(authorizedRoles)) {
event.preventDefault();
$state.go($state.current, {}, {reload: true});
$rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
}
}
if (!AuthService.isAuthenticated()) {
if (next.name !== 'login') {
event.preventDefault();
$state.go('login');
}
}
});
you can achive this by adding one boolean parameter in data property of .state, let say requiresAuth and check that also in .run block;
below are pseudo code for that
in .config block
$stateProvider
.state("register", {
url: '/register',
templateUrl: 'register.html',
controller:'UserController',
controllerAs: 'vm',
data: {
requiresAuth: false,
pageTitle: 'Register'
}
})
.state("dashboard", {
url: '/dashboard',
templateUrl: 'dashboard.html',
controller:'OtherController',
controllerAs: 'vm',
data: {
requiresAuth: true,
pageTitle: 'Dashboard',
authorizedRoles: ['WHATEVER_ROLE']
}
});
and in .run block
var stateChangeStart = $rootScope.$on('$stateChangeStart', function(event, toState, toParams) {
if (AuthService.isAuthenticated()) {
// if user trying to access register/forgot page after login than redirect to dashboard
if (!toState.data.requiresAuth) {
event.preventDefault();
$rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
}
// user is not authenticated and trying to access page which is not permissible than send back to dashboard
if (angular.isDefined(toState.data.authorizedRoles)) {
var roles = toState.data.authorizedRoles;
AuthService.isAuthorized(roles).catch(function() { // NOTE: here we are only handling with .catch block
event.preventDefault();
$rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
});
}
}
// user is not authenticated than redirect to login
else if (toState.data.requiresAuth) {
event.preventDefault();
$rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
}
});
var notAuthenticated = $rootScope.$on(AUTH_EVENTS.notAuthenticated, function() {
$log.warn('not authenticated');
$state.go('login', null, {});
return;
});
var notAuthorized = $rootScope.$on(AUTH_EVENTS.notAuthorized, function() {
$log.warn('not authorized');
$state.go('dashboard');
return;
});
// DO NOT forget to destroy
$rootScope.$on('$destroy', notAuthenticated);
$rootScope.$on('$destroy', notAuthorized);
$rootScope.$on('$destroy', stateChangeStart);

AngularJS ui router $stateChangeStart with promise inifinite loop

I'm trying to build some sort of authentication in my angular app and would like to redirect to a external URL when a user is not logged in (based on a $http.get).
Somehow I end up in an infinite loop when the event.preventDefault() is the first line in the $stateChangeStart.
I've seen multiple issues with answers on stackoverflow, saying like "place the event.preventDefault() just before the state.go in the else". But then the controllers are fired and the page is already shown before the promise is returned.
Even when I put the event.preventDefault() in the else, something odd happens:
Going to the root URL, it automatically adds the /#/ after the URL and $stateChangeStart is fired multiple times.
app.js run part:
.run(['$rootScope', '$window', '$state', 'authentication', function ($rootScope, $window, $state, authentication) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
event.preventDefault();
authentication.identity()
.then(function (identity) {
if (!authentication.isAuthenticated()) {
$window.location.href = 'external URL';
return;
} else {
$state.go(toState, toParams);
}
});
});
}]);
authentication.factory.js identity() function:
function getIdentity() {
if (_identity) {
_authenticated = true;
deferred.resolve(_identity);
return deferred.promise;
}
return $http.get('URL')
.then(function (identity) {
_authenticated = true;
_identity = identity;
return _identity;
}, function () {
_authenticated = false;
});
}
EDIT: Added the states:
$stateProvider
.state('site', {
url: '',
abstract: true,
views: {
'feeds': {
templateUrl: 'partials/feeds.html',
controller: 'userFeedsController as userFeedsCtrl'
}
},
resolve: ['$window', 'authentication', function ($window, authentication) {
authentication.identity()
.then(function (identity) {
if (!authentication.isAuthenticated()) {
$window.location.href = 'external URL';
}
})
}]
})
.state('site.start', {
url: '/',
views: {
'container#': {
templateUrl: 'partials/start.html'
}
}
})
.state('site.itemList', {
url: '/feed/{feedId}',
views: {
'container#': {
templateUrl: 'partials/item-list.html',
controller: 'itemListController as itemListCtrl'
}
}
})
.state('site.itemDetails', {
url: '/items/{itemId}',
views: {
'container#': {
templateUrl: 'partials/item-details.html',
controller: 'itemsController as itemsCtrl'
}
}
})
}])
If you need more info, or more pieces of code from the app.js let me know !
$stateChangeStart will not wait for your promise to be resolved before exiting. The only way to make the state wait for a promise is to use resolve within the state's options.
.config(function($stateProvider) {
$stateProvider.state('home', {
url: '/',
resolve: {
auth: function($window, authentication) {
return authentication.identity().then(function (identity) {
if (!authentication.isAuthenticated()) {
$window.location.href = 'external URL';
}
});
}
}
});
});
By returning a promise from the function, ui-router won't initialize the state until that promise is resolved.
If you have other or children states that need to wait for this, you'll need to inject auth in.
From the wiki:
The resolve keys MUST be injected into the child states if you want to wait for the promises to be resolved before instantiating the children.

How to refresh resolve?

Using ui-router, I have a state with a resolve function:
.state('tab.social', {
url: '/social/',
views: {
'menuContent': {
templateUrl: 'templates/social/tab-social.html',
controller: 'SocialCtrl',
resolve: {
socialAuthResolve: socialAuthResolve
}
}
}
})
I capture the resolve in the controller as follows:
.controller('SocialCtrl', function($scope, SocialAuth, socialAuthResolve) {
//
console.log(socialAuthResolve);
//
$scope.logOut = function() {
SocialAuth.logOut();
$state.go('tab.social', {}, {reload: true});
};
//
$scope.logIn= function() {
SocialAuth.logIn();
$state.go('tab.social', {}, {reload: true});
};
})
However, when either pressing logOut or logIn, my state is not refreshed. I am using some parameters from the resolve socialAuthResolve and would like this to be updated. In this case, the old parameters are still in there.
Only when I refresh my browser, then the variables and the page are updated accordingly. How can I refresh the page after the logOut and logIn? For instance, force to resolve again?
Here is a sample state with config:
.state('app.stateName', {
url: "/theUrl",
views: {
'myViewName': {
templateUrl: "templates/template.html",
controller: 'SomeController',
resolve: {
pleaseResolve: function() {
console.log("I am resolved");
}
}
}
}
})
In my controller (assuming SomeController as mentioned above), whenever I enter into the state I run this.
var res = ($state.$current.self.views.myViewName.resolve.pleaseReslove)
res.call()
This will call my resolve function every time I come into the view.

How to unit test onEnter and resolve of ui-router state

I'm trying to test an Angular UI Bootstrap modal that is being called from the onEnter of the following state:
.state("profile.index.edit.services", {
url: "/edit/services/:serviceCode",
parent:"profile.index",
onEnter: ['$stateParams', '$state', '$modal',function($stateParams, $state, $modal) {
$modal.open({
templateUrl: 'profile/edit-services-modal.html',
controller: 'ProfileEditServicesController',
resolve: {
profileData: function(CacheService){
return CacheService.getItem(CacheService.Items.Profile.loadedProfile);
},
allServicesList: function(ProfileService){
return ProfileService.getAllServicesList($stateParams.serviceCode,$stateParams.orgId);
},
serviceCode: function() {
return $stateParams.serviceCode;
}
}
}).result.then(function(result) {
if (result) {
return $state.transitionTo("profile.index", { orgId: $stateParams.orgId });
}
else { // cancel
return $state.transitionTo("profile.index", { orgId: $stateParams.orgId }); // don't reload
}
}, function () {
// executes on close/cancel/ESC
return $state.transitionTo("profile.index", { orgId: $stateParams.orgId });
});
}]
})
I've been banging my ahead against this trying many different things to set the test up, but can't seem to figure it out. Any plunkers or suggestions are greatly appreciated!!!!
By the way, the state above is a descendant of these states, which I've been able to write tests for that pass:
.state('profile.index', {
url: '/:orgId',
views: {
'profile-index#profile': {
templateUrl: 'profile/view-profile.html',
controller: 'ProfileController'
},
'header#': {
templateUrl: 'common/layout/header.html',
controller: 'HeaderController'
},
'footer#':{
templateUrl: 'common/layout/footer.html',
controller: 'FooterController'
}
},
resolve: {
profileData: function(ProfileService, $stateParams){
return ProfileService.getProfile($stateParams.orgId, true);
}
}
})
.state('profile.index.edit', {
url: '',
abstract: true
})
did you ever figure this out? you should have passed a named controller, that way you could test that controller (since it would just be a function) directly instead of relying on onEnter being fired.

Categories

Resources