I have a project using AngularJS with ui-router. Everything working fine beside the redirect from login screen to preview state on the first load.
Default state is home - http://test/#/
For example, if user is not logged in - http://test/#/test/1000/details goes to login page (which it is supposed to do)
Then after user login, the system goes to default state "home - http://test/#/" but I want to go to http://test/#/requests/1000/details
How do I save the "http://test/#/requests/1000/details or stateName" to redirect after login?
I try using $stateChangeSuccess to save a log of states in the $rootscope but the first one (http://test/#/requests/1000/details) never gets saved.
Any ideas how to handle this?
Thanks.
You can add a 'previous' property to $state in your main-app's run method.
This is how I solved the problem:
// add ui-router variables to $rootScope. Comes handy in many cases, for example setting page title
angular.module('app').run(['$rootScope', '$state', '$stateParams', addUIRouterVars]);
function addUIRouterVars($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
// add previous state property
$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState) {
$state.previous = fromState;
});
}
Related
I'm having the classic problem where the $locationChangeStart event fires multiple times. I've tried to use the preventDefault but I can't seem to master this.
Two problems - first scenario uses $location and the second uses $state:
Once user is authenticated, I redirect using $state.go('main') , however $locationChangeStart is fired again. I do not want this behavior.
With the ui-router $stateChangeStart event, I basically hit an infinite loop scenario. Meaning that, when this event fires I check for user authentication. If the user is NOT authenticated, I then redirect using $state.go('login');. This causes an infinite loop.
Here's my app.js for starters:
(function () {
'use strict';
angular.module('rage', [
'ui.router',
'ui.bootstrap',
'ui.dashboard',
'kendo.directives'
]).run(['$rootScope', '$state', '$location', 'userService', 'loginService', init]);
function init($rootScope, $state, $location, userService, loginService) {
$rootScope.rageSessionVars = {};
$rootScope.$state = $state;
$rootScope.isLoggedin = false; // just something to possibly use ???
$rootScope.$on('$locationChangeStart', function () {
var userToken = loginService.getUserCookie();
// check if cookie expired, then authenticate user !
if (userToken) {
if (!loginService.isUserCookieExpired(userToken)) {
if (loginService.authUser(userToken)) {
$state.go('main');
}
else {
$location.url('index.html#/?login'); // not authenticated !
}
}
}
else {
$state.go('login');
$location.url('index.html#/?login');
}
});
}
})();
I've also created a plunker for a similar scenario yesterday, and simply modified the app.js to now include the locationChangeStart event.
Online plunker here: http://plnkr.co/edit/hsSrPqFp0hpJ4A8cTsVL?p=preview
Bottom line is I'd like to hook into one of these event for a smooth user Login/Logout experience, but I always end up in a snag with these Angular events.
Thank you in advance for your expert tips.
regards,
Bob
While using angular ui router, use $stateChangeStart and $stateChangeSuccess to control actions like authentications and manual routing during state changing.
I want to redirect the user to a language page and not allow him to the index page if he hasn't chosen a language yet. I'm using the Angular Translate module. This module has cookie usage built-in with the following function:
$translateProvider.useCookieStorage();
This works. Now I would like to know if this cookie is set, and if not, redirect the user to the language page. Here's my idea how to handle this.
.factory('cookiecheck', function(){
if($translateProvider.CookieStorage.get) return true;
return false;
}
.run(function($rootScope, $state){
if(cookiecheck
{$location.path('/language');}
else
{$location.path('/home');}
This doesn't work. How would I best approach this? What is the correct syntax to determine if a CookieStorage exists and redirect?
You have a few syntax errors in your posted code, the factory isn't necessary as you can inject the $translate service into your run block.
.run(function($rootScope, $state, $location, $translate) {
var store = $translate.storage();
// if store is empty?
if(angular.equals(store, {}))){
$location.path('/language');
}
else {
$location.path('/home');
}
});
See ng-translate docs for cookie store
Also, since you won't know if and when the cookie will be expired or removed i think it is best to watch the route for changes and do your check then.
To do that hook into the $stateChangeStart event from ui router in your run block
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
// check then redirect
});
ui router docs
see this post for watching route changes
Let's say I have 4 routes - 2 require the user to be logged in, 2 do not. My app init looks like:
$routeProvider.when('/open1',{templateUrl:'/open1.html',controller:'Open1'});
$routeProvider.when('/open2',{templateUrl:'/open2.html',controller:'Open2'});
$routeProvider.when('/secure1',{templateUrl:'/secure1.html',controller:'Secure1'});
$routeProvider.when('/secure2',{templateUrl:'/secure2.html',controller:'Secure2'});
Routes /open1 and /open2 are open to all, while routes /secure1 and /secure2 require the user to be logged in and, if not, take some action, e.g. redirect to login or launch a warning. I can determine the user's state by using my Auth service and calling, e.g., Auth.isLogin(). So the result would be:
going to /open1 and /open2 always go to the template and controller declared above
if Auth.isLogin() returns true, /secure1 and /secure2 go to the above-declared template and controller
if Auth.isLogin() returns false, /secure1 and /secure2 take some other action, e.g. $location.path('/login')
I could put logic in the Secure1 and Secure2 controllers that checks, but that is repetitive and mixes up responsibilities, makes them harder to test, etc.
Is there a way that I can use the $routeProvider to declare, "check this route and this route and if not, redirect"? I was thinking of using resolve somehow, but not quite sure how to work it in (docs on resolve are not very clear, and few helpful examples).
EDIT:
based on the answers below, it appears there are three philosophies for doing this:
Using resolve to check logged in and fail the promise, and then catching the $routeChangeError event to redirect http://www.sitepoint.com/implementing-authentication-angular-applications/
Using just $routeChangeStart event to check logged in and redirect http://arthur.gonigberg.com/2013/06/29/angularjs-role-based-auth/
Using just resolve to check logged in and redirect http://midgetontoes.com/blog/2014/08/31/angularjs-check-user-login
The 2nd option is what the two answerers have suggested.
As in my comments above, there are 3 different paths (plus the ability to use a directive if you want to control it from within html templates). I ended up following
https://midgetontoes.com/angularjs-check-user-login/
which essentially is as follows:
$routeProvider.when('/secure', {
templateUrl: '/secure.html',
controller: 'Secure',
resolve:{
loggedIn:onlyLoggedIn
}
});
And then onlyLoggedIn:
var onlyLoggedIn = function ($location,$q,Auth) {
var deferred = $q.defer();
if (Auth.isLogin()) {
deferred.resolve();
} else {
deferred.reject();
$location.url('/login');
}
return deferred.promise;
};
Simple, works like a charm. If I ever need a directive, I will pull this piece into a service.
This blog post deals with user authentication in AngularJS using directives.
The $route service emits $routeChangeStart before a route change.
If you don't use directives, you can catch that event by calling app.run (you can place it after the code where you define the routes [app.config]). For example:
For full disclosure I use ui.router and this is an adapted code from $stateChangeStart I use in my app
var app = angular.module('app');
app.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/open1',{templateUrl:'/open1.html',controller:'Open1'});
$routeProvider.when('/open2',{templateUrl:'/open2.html',controller:'Open2'});
$routeProvider.when('/secure1',{templateUrl:'/secure1.html',controller:'Secure1'});
$routeProvider.when('/secure2',{templateUrl:'/secure2.html',controller:'Secure2'});
}]);
app.run(['$rootScope', '$location', 'Auth', function($rootScope, $location, Auth) {
$rootScope.$on('$routeChangeStart', function(event, currRoute, prevRoute){
var logged = Auth.isLogin();
//check if the user is going to the login page
// i use ui.route so not exactly sure about this one but you get the picture
var appTo = currRoute.path.indexOf('/secure') !== -1;
if(appTo && !logged) {
event.preventDefault();
$location.path('/login');
}
});
}]);
I had the same problem and I did it this way:
var app = angular.module('myModule',["ui-bootstrap"]);
And then listen for a locationchange in the app (this will also trigger onEnter of a page)
app.run(function ($rootScope, $location, $cookieStore) {
$rootScope.$on("$locationChangeStart", function (event, next, current) {
//Here you can check whatever you want (servercall, cookie...)
});
}
I Hope this helps!
On first page load when app gets initialized I want to redirect the user to login page. I think the relevant part of the code is this
$rootScope.$on("$routeChangeStart", function (event, next, current) {
alert("change location");
$location.path('/login');
});
It is based on https://github.com/fnakstad/angular-client-side-auth/blob/master/app/js/app.js The problem is on page load the alert is triggered but location does not change. I have to click on a navigation item of my app and then the login action will be called and the route changes.
.run(['$rootScope', '$location', '$cookieStore', function ($rootScope,
$location, $cookieStore) {
$location.path('/login');
$rootScope.$on("$routeChangeStart", function (event, next, current) {
$location.path('/login');
});
$rootScope.appInitialized = true;
}]);
This will work however seems redundant. And why is alert triggered but not location change?
Why does the location not changes on full page load? How to fix this?
Full code http://jsfiddle.net/qfSC3/ but fiddle does not work.
Try using the $locationChangeStart event instead
$scope.$on("$locationChangeStart", function(event){
event.preventDefault();
})
Based off this question: AngularJS - Detecting, stalling, and cancelling route changes
I use angularjs with ui-router library. Lets say I have some routes for admin and some routes for user. If admin or user is logged in I want to show some page for them (admin.html for admin and user.html for user, for example), otherwise login.html
On the backend I have a special url, like /auth/status/, which gives me information about the user (if he's logged and which role he has)
There are some situations I can't figure out how to handle:
I go to '/' url. The application loads. I have a run method for my app module. But how can I check if the user is logged in, when it happens asynchronously? Well, I have this and it works somehow, but I'm not sure if this is a good solution:
app.config(['$stateProvider', '$routeProvider',
function($stateProvider, $routeProvider) {
$stateProvider
.state('admin', {
abstract: true,
url: '/',
templateUrl: 'templates/admin.html'
})
.state('admin.desktop', {
url: 'desktop',
templateUrl: 'templates/desktop.html'
});
}]);
app.run([
'$http',
'$rootScope',
'$location',
'$state',
'userRoles',
function($http, $rootScope, $location, $state, userRoles) {
var DEFAULT_ADMIN_STATE = 'admin.desktop';
var promise = $http.get('/auth/status/');
promise.then(function(response) {
$rootScope.isLogged = response.data.logged;
$rootScope.userRole = userRoles[response.data.role];
if (!$rootScope.isLogged) {
$state.transitionTo('login');
} else {
switch (response.data.role) {
case 'admin': $state.transitionTo(DEFAULT_ADMIN_STATE); break;
}
}
}, function(response) {
$location.path('/login');
});
}]);
Though I don't understand: if I go to / url I should get an error because it's abstract. Instead when $http.get request is resolved (I put 2 seconds sleep in backend to check that) I transition to admin.desktop state. I'm confused what happens in which order: state loads template or app.run function with some ajax requests...
The main question is, when I go to /#/desktop how can I first check if user is logged (send a request to /admin/auth/ and check what it returns) and only then decide what to do (transition to login or desktop state)?
I found Delaying AngularJS route change until model loaded to prevent flicker this, but again still a little fuzzy for me. Resolve property seems like a solution when I want to load a list of entities and then show the template. But I want to have some "before" function for ALL states which just checks if user is logged and has a correspond role (one moment: I do not want to use /admin/entites or /user/entities urls, want to have just /entitites. As I get it several states may have the same url). So basically it looks like if I go to /someurl I want to run method wait until it gets ajax response and after that transition to some state. Instead the state corresponding to /someurl load a template...
Also I found an article about authentication in angular but author uses cookies which is not async thing
Update: when I use cookies for checking if user is logged and I go to /#/desktop I still have it rendered, and $state.transitionTo doesn't work..
You should check it before page load:
I cannot write full example now, but in common you should do like this:
.run([ ..., function(....) {
$scope.$on('$routeChangeStart', function(next, current) {
... check cookie here ...
});
}]);