set condition on otherwise stateProvider in angularJS - angularjs

I am working on a project and it needs once a user entered his ID he wont be able to see the login page again when he will use the app.
I am not able to understand how to do this logic which I had tried is not working.
can anyone help me on this.This is my link of project on google drive have a look:
https://drive.google.com/open?id=0B51wL8pUai8XSHc0eHFQQ1cxX2M

You need to use localStorage in this case, where once you successfully login set your current user to true and on logout set that flag to false.
and over your app.run block you can put a condition.
$rootScope.$on('$stateChangeStart', function (event, toState, toParams,fromState, fromParams) {
if (toState.name !== 'login') {
if (!localStorageService.get('currentUser') {
event.preventDefault();
$state.go('login');
}
} else {
return false;
}
}

Related

Back button confirm doesn't work in AngularJS app

In one page of my AngularJS app I want to display a confirm if the user click on the browser back button :
So I added a listener on stateChange to detect this behavior, and if the user don't want to go back I cancel it.
Here is my piece of code :
angular
.module('app.platform')
.run(start);
start.$inject = ['$rootScope', '$state'];
function start ($rootScope, $state) {
// Save navigation
$rootScope.previousState = null;
// State change listener
$rootScope.$on('$stateChangeStart', stateChangeStartListener);
function stateChangeStartListener(event, toState, toParams, fromState, fromParams) {
// app.platform.quiz is the state that I want to display a confirm if the user
// uses the back button
// Then I record and check the previous state reached to know that the user uses the back button
if(fromState.name === 'app.platform.quiz'
&& $rootScope.previousState
&& $rootScope.previousState === toState.name) {
if(confirm("Do you really want ot leave the quiz ?")) {
$rootScope.previousState = fromState.name;
$state.go(toState, toParams);
} else {
event.preventDefault();
}
} else {
$rootScope.previousState = fromState.name;
}
}
}
When the user hits OK button it works well, but when he hits Cancel I got a strange behaviour. He stays on the page, but if he hits the back button again nothing happen (no confirm, no go back), and if he hits back button again he go back 2 states before.
It sounds like my event.preventDefault() mess with the history.
I'm not very familiar with the HTML5 History API, but maybe it can solve my problem.
Do you have any idea what I'm doing wrong ? Or why I got this behaviour (and how to correct it).
EDIT :
After some reading (from comments), I know what is the problem, and how to solve it (only in theory).
I need to catch the back button click before it change the URL, but I think it's not possible.

AngularJS UI routing $stateChangeStart dont work as expected

I have created an app with angular ui routing and states. Also I have a WEB API controller that can use to learn if user is authenticated or not. The problem is that http request to that WEB API has some delay to return result because is asynchronous.
I want, every time when user wants to go on state, to check if is authenticated or not, and give him access or redirect to login page. But first time when app running with someway i want to wait until i have answer from my WEB API.
I have this part of code
PlatformWeb.run(function ($rootScope, $state, $timeout, Authorization) {
$rootScope.$state = $state;
$rootScope.User = {
isAuthenticated: null,
targetState: null
}
Authorization.isAuthenticated().then(function (result) {
$rootScope.User.isAuthenticated = result;
$state.go($rootScope.User.targetState.name);
})
hasAccess = function () {
return $rootScope.User.isAuthenticated;
}
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
$rootScope.User.targetState = toState;
if ($rootScope.User.isAuthenticated == null)
event.preventDefault();
else {
if (!hasAccess()) {
event.preventDefault();
$state.go('PlatformWeb.Login');
}
}
});});
The first time when app runs, $rootScope.User.isAuthenticated is null, so i will prevent to load state. Also with my 'Authorization' service i call my asynchronous function to get if user if authenticated or not. Before i prevent loading state i keep when user wants to go, so when i have my result back from WEP API, I change the state '$state.go($rootScope.User.targetState.name);' to the state he want. Then I know if is authenticated or not and i ask if has permissions to go. If he hasn't then i redirect him to login state.
BUT event.preventDefault(); doesn't work as expected. When i run my app i get this error 'angular.min.js:6 Uncaught Error: [$rootScope:infdig] http://errors.angularjs.org/1.3.7/$rootScope/infdig?p0=10&p1=%5B%5D' multiple times.
Angular documentation says :
'10 $digest() iterations reached. Aborting!
Watchers fired in the last 5 iterations: []'
In my logic, 'stateChangeStart' function in condition '$rootScope.User.isAuthenticated == null' with event.preventDefault(); will make app logs here, and not run more until i know. When i get my result back from my WEBAPI I will go again in this function, but this time i know in which state to send him.
I would suggest, firstly always check where is current change navigating to... and get out if it is already redirected
$rootScope.$on('$stateChangeStart',
function (event, toState, toParams, fromState, fromParams) {
// are we already going to redirection target?
if(toState.name === 'PlatformWeb.Login'){
return; // yes, so do not execute the rest again...
}
$rootScope.User.targetState = toState;
if ($rootScope.User.isAuthenticated == null)
event.preventDefault();
else {
if (!hasAccess()) {
event.preventDefault();
// we will redirect here only if not already on that way...
$state.go('PlatformWeb.Login');
...

How to make a function wait for promise and return synchronously?

I know that generally you don't want to do this, but I've found a use case where I can't seem to find any other solution and want to have my function which contains a promise wait until the promise resolves before returning anything.
I'm modifying the authentication mechanism for an application and have the following code set up to double check that the user is authorized whenever the $state changes, and redirect them back to the login screen if they are not authorized:
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
// double check that the user is logged in
if (toState && toState.name.indexOf('auth')<0) {
if (!user.isAuthorized()) {
event.preventDefault(); // do not continue to the non-auth state
user.logout();
$state.go("auth.login");
}
}
});
Currently, the user.isAuthorized() function simply returns a true or false value from an internal boolean that is set/unset when the user logs in or out, and everything works fine.
However, we need to make that function call out to the server (if the local boolean is set to false) to check if the user already has an active session and then set the boolean and return true. We don't want the application to do anything at all until that server call returns, since it is checking to see if the user should be able to see the UI at all.
I have attempted to utilize a promise to handle this case, but it has unexpected results because the event.preventDefault() call doesn't get called immediately (only after the promise resolves), at which point the $state has already changed. At a minimum this causes the UI to flash briefly before being sent to the login screen, and in some cases those other $states make their own server calls when loaded which fail and/or cause the browser to prompt the user for basic authorization (username/password).
This does not work:
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
// double check that the user is logged in
if (toState && toState.name.indexOf('auth')<0) {
user.isAuthorized().then(function(isAuthorized) {
if (!isAuthorized()) {
event.preventDefault(); // this does not prevent state change any more
user.logout();
$state.go("auth.login");
}
});
}
});
I think we need to have the user.isAuthorized() function look something like this:
user.isAuthorized = function() {
if(user.isAuthorizedBoolean) {
return true;
} else {
var returnValue = false;
// make server side call that returns promise
user.isAuthorizedServerCheck().then(function(result) {
if (result === true) {
returnValue = true;
}
});
// pause execution until promise resolves???
return returnValue;
}
};
But I'm not sure how to accomplish that.
It looks like the solution posted here: Confusing $locationChangeSuccess and $stateChangeStart by #RadimKöhler is what I'm looking for.

Angularjs authentication redirect issue

I am having an app that every page except login requires authentication. I do this by checking the $stateChangeStart and then redirecting to /login when token is not set. on initial app load this works fine, but at login screen if i type another "restricted" url. It does the check, changes the url, but still loads the restricted page. What am I missing here?
//app.run:
app.lazy = $couchPotato;
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$rootScope.$on('$stateChangeStart', function(event, toState, toStateParams) {
console.log("state changed to "+toState.name);
console.log(toState);
Authentication.validateLogin(toState, toStateParams);
});
Authentication.validateLogin:
validateLogin:function(toState, toStateParams){
$rootScope.toState = toState;
$rootScope.toStateParams = toStateParams;
if ($localStorage.token == null) {
console.error("Not Authenticated!");
$location.url('/login');
}
}
I see that your using angular-ui, so I'm not exactly sure what advantages that has over using 'basic' angular, but I wrote this to handle validating a token when the route changes.
app.run(['$rootScope', 'authService',
function ($rootScope, authService) {
$rootScope.$on("$routeChangeSuccess", function (event, next, current) {
if (next && next.$$route && authService.isAuthenticated()) {
authService.validate();
$rootScope.appTitle = next.$$route.title;
}
});
}]);
The $routeChangeSuccess handles navigation to routes after the controller has loaded (very important for objects that load when the page loads when validation is confirmed), and then validates them.
This also performs 2 checks in that it checks if the token in local storage exists and is formed correctly, and if it is, send it back to the server to confirm that.
I also found that I had to use the $locationChangeStart to handle page refresh, to re-validate when someone tries to refresh the page.
The validateLogin solution:
validateLogin:function(toState, toStateParams){
if(toState.access){
if(toState.access.loginRequired){
if ($localStorage.token == null) {
$state.go('login');
}
}
}
Try this with the validateLogin:
validateLogin:function(toState, toStateParams){
if(toState.access){
if(toState.access.loginRequired){
if ($localStorage.token == null) {
$state.go('login');
}
}
}
}

Ui-Router $state.go inside $on('$stateChangeStart') is cauzing an infinite loop

I'm trying to introduce login into the way the user navigates accross the application.
I pretend to redirect the user to the page were he was before he navigate to the login page if that page meets specific requirements
Preventing the event from the $stateChangeStart stop's the state change like expected but when i run the $state.go('into_somewhere') i enter an infinit loop
My angular version is 1.3.1 and the ui-router is the latest
.factory('RouteHistory', function ($rootScope,$log, $state, Auth, $urlRouter, $timeout) {
// after the user enter a page
var currentState = '';
// when the user is trying to access a page that he has not permissions
// or that requires the user to be logged in
var pendingState = '';
var isMenuTogglerVisible = false;
var skipFromStateVal = true;
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
event.preventDefault();
if (toState.name == 'login' && fromState.name != 'login'){
$log.log('Ui-router: changing to login');
// $urlRouter.sync();
$state.go('login')
//pendingState = fromState;
//$log.log('Peding state updated to:' + pendingState.name );
//$urlRouter.sync();
}
if (fromState.name == 'login' && Auth.isLoggedIn()) {
$log.log('Ui-router: going from login');
//$state.go(fromState.name);
$timeout(function(){
// $state.go('home', null, {/*reload: true, location: 'replace'*/});
$state.go('browse-machine');
//$urlRouter.sync();
},2000)
}
$log.log({
'toState': toState,
'toParams': toParams,
'fromState': fromState,
'fromParams': fromParams
})
})
return {
};
});
In general I would say, let's redirect ($state.go()) only if needed. In other cases, get out from the event listener:
if (toState.name === 'login' ){
// doe she/he try to go to login? - let him/her go
return;
}
if(Auth.isLoggedIn()){
// is logged in? - can go anyhwere
return;
}
// else
$state.go('login')
This is simplified logic, but shows, that we should change to execution only if needed. There are some other examles with more detailed implementation and plunkers:
Confusing $locationChangeSuccess and $stateChangeStart
Angular UI Router: nested states for home to differentiate logged in and logged out
other example of log in
angular ui-router login authentication
As provided in the comment, there was plunker, which I changed like this here
...
// three new lines
if (toState.name === 'specialRoute'){
return;
}
if (fromState.name=='route1'){
event.preventDefault();
$state.go('specialRoute')
}
And this is not looping anymore. Please, check it here
You should use the notify option :
$state.go('your.state',{ your params },{notify: false});
This will prevent stateChangeStart to fire again.
This answer helped me:
$urlRouterProvider.otherwise( function($injector, $location) {
var $state = $injector.get("$state");
$state.go("app.home");
});
Original:
Why does AngularJS with ui-router keep firing the $stateChangeStart event?
I simply used $location.path('every/where') instead of $state.go('every/where')
:) .
The infinite loop is partly caused by
if (toState.name == 'login' ...) { $state.go('login'); ...
..which says if you're going to the login state, then go to the login state.
...And calling event.preventDefault() as the first line in the event handler doesn't help. When you use go() to go to the login screen (or anywhere else), that state change is also prevented by event.preventDefault(). It should only be used within an if.
Your entire $stateChangeStart handler should instead be...
if (!Auth.isLoggedIn() && toState.name != 'login') {
event.preventDefault();
Auth.desiredState = toState.name;
$state.go('login');
}
...which reads naturally. "If you're not logged in and you're not already going to the login screen, then stop what you're doing, I'll remember where you wanted to go, and you now go to the login screen."
Later your Auth object will issue a $state.go(Auth.desiredState) when it's satisfied with the user.
It works for me, Below code helps to get rid of infinite loop
let firstPass = true;
$scope.$on('$stateChangeStart', function(event, toState, toParams) {
if ($scope.addForm.$dirty && firstPass) {
event.preventDefault();
ConfirmationDialog.openNavigateAwayConfirmationModal().then(function () {
firstPass = false;
return $state.go(toState, toParams);
});
firstPass = true;
}
});

Resources