I'm looking for a best way to redirect my users from login/register form if they are already properly authenticated (and vice-versa)
So is it a good idea to do it onEnter? Will that function be executed before the associated controllers? Example:
...
.state('auth', {
templateUrl: "auth.html",
controller: 'PBAuthController as PBAuth',
onEnter: skipIfLoggedIn
})
...
function skipIfLoggedIn($q, $auth, $state) {
var deferred = $q.defer();
if ($auth.isAuthenticated()) {
$state.go('backend');
} else {
deferred.resolve();
}
return deferred.promise;
}
I just did a quick test and onEnter fires before the controller however there doesn't appear to be any way to prevent the controller from being instantiated.
In any case, here's how I've handled redirects in onEnter. The only thing to watch out for is to not interrupt the current state transition. You do this by waiting for the $state.transition promise to resolve...
onEnter: function($state, $auth) {
return $auth.isAuthenticated() && $state.transition.then(function() {
return $state.go('backend');
});
}
Related
I have defined a factory auth as follows:
app.factory('auth', [function () {
var auth = {};
auth.getUser = function () {
... ...
// get user from cookie, cache, history, etc.
}
return auth
}]);
For many pages, I want to always run auth.getUser() before displaying them. To this end, at the moment, I use resolve, eg:
.state('home', {
url: '/home',
templateUrl: '/htmls/home.html',
controller: 'MainCtrl',
resolve: { getUser: ['auth', function (auth) { return auth.getUser() }]}
})
The pitfall of this implementation is I have to write lots of resolve. Thus, I am wondering if there is a way to implement this rule in controllers rather than state. Could anyone help?
Try checking the condition in
$rootScope.$on('$stateChangeStart', function (event, toState) {
//condition here and $state.go('')
});
I'm not sure if this works.
use angular run function
app.run(function() {
alert(1);
});
see http://jsfiddle.net/ysq3m/
I need to restrict the user from redirect and need to login only with authentication.
I tried but I can redirect to login page using back button and again come to same page using forward button. Even I can go to the required page using URL without login.
My code :
config.$inject = ['$routeProvider', '$locationProvider'];
function config($routeProvider, $locationProvider ) {
$routeProvider
.when('/login', {
controller: 'LoginController',
templateUrl: 'view/login.view.html',
controllerAs: 'vm'
})
.when('/profileData', {
controller: 'profileDataController',
templateUrl: 'view/profiledata.view.html',
controllerAs :'vm'
})
.when('/questionBank', {
controller: 'questionbankController',
templateUrl: 'view/questionbank.view.html',
controllerAs: 'vm'
})
.when('/dashboard', {
// controller: 'PersonalInfoController',
templateUrl: 'view/dashboard.view.html',
controllerAs:'vm'
})
.otherwise({ redirectTo: '/login' });
}
run.$inject = ['$rootScope', '$location', '$cookieStore', '$http'];
function run($rootScope, $location, $cookieStore, $http) {
// keep user logged in after page refresh
$rootScope.globals = $cookieStore.get('globals') || {};
if ($rootScope.globals.currentUser) {
$http.defaults.headers.common['Authorization'] = 'Basic ' + $rootScope.globals.currentUser.authdata; // jshint ignore:line
}
$rootScope.$on('$locationChangeStart', function (event, next, current) {
//redirect to login page if not logged in and trying to access a restricted page
var restrictedPage = $.inArray($location.path(), ['/dashboard','/questionBank', '/profileData']) === -1;
/* var a = $location.$$absUrl.split('#')[1];
var patt = new RegExp(a);
var res = patt.test(restrictedPage); */
var loggedIn = $rootScope.globals.currentUser;
if (restrictedPage && !loggedIn) {
$location.path('/login');
}
});
}
use this :based on response from server
.when('/login', {
controller: 'LoginController',
templateUrl: 'view/login.view.html',
resolve:{
logincheck: checklogedin
})
/ resolve function for user....
var checklogedin = function($q ,$http,$location)
{
var deferred =$q.defer();
$http.get('/loggedin').success(function(user){
if (user.staus==true)
{
//goo
deferred.resolve();
}
else
{
deferred.reject();
$location.url('/login');
}
});
return deferred.promise
};
Based on the code that you have provided, I can't tell 100% what is going on in your code. But... you could always try to use the resolve property on each route that you don't want to allow access without authentication. Here is what that would look like for questionBank:
.when('/questionBank', {
controller: 'questionbankController',
templateUrl: 'view/questionbank.view.html',
controllerAs: 'vm',
resolve: {
auth: function(AuthService, $q){
if(AuthService.isAuthenticated()) return $q.resolve();
return $q.reject();
}
}
})
Each property of the resolve object should return a promise, and if that resolves... the route change works. If it rejects... the route change is not allowed. If the promise never resolves, you are screwed, so make sure it resolves or it will never do the route.
This isn't the only way to try what you are saying. It is A way of trying it.
You can also add event listener on your $scope and prevent moving in case of unauthenticated user.
$scope.$on('$locationChangeStart', function (event, next, current) {
if (!is_logged_in) {
event.preventDefault();
}
});
In my code I have two main controllers LoginCtrl and AppCtrl, and all other controllers are nested within AppCtrl. Then in AppCtrl I have this code, which will check for logged user.
if (localStorageService.get('authToken') === null) {
$state.go('login', {locale: CONFIG.defaultLang});
} else if (!userService.isLoggedIn()) {
tokenStorage.setAuthToken(localStorageService.get('authToken'));
userService.setIdentity(JSON.parse(localStorageService.get('user')));
}
As you can see I store auth token from server in local storage. When page loades this code will be executed and if you are not logged in you will be redirected. And because all other application controllers are nested within AppCtrl this code will be executed every time.
For more info about nested controllers try for example this article - https://rclayton.silvrback.com/parent-child-controller-communication
In my angularjs app, I am checking if user lands on landing page and is already authenticated, then redirect him to home page.
.state('landingpage', {
abstract: "true",
url: "/landingpage",
templateUrl: "app/landingpage/landingpage.html",
resolve: {
AutoLoginCheck: ['$state', '$window', function ($state, $window) {
if($window.localStorage.access_token != null)
{
if($window.sessionStorage.access_token == null) {
$window.sessionStorage.access_token = $window.localStorage.access_token;
}
UserInfoService.SetUserAuthenticated(true);
// it is not redirecting
return $state.go('app.home');
}
}]
}
})
The problem is that though all the resolve code is successfully run, user is not getting redirected to app.home. Can someone tell why this happens?
Note: The state 'app' also has a resolve in which it fetches the data to be displayed in 'app.home' state.
.state('landingpage', {
abstract: "true",
url: "/landingpage",
templateUrl: "app/landingpage/landingpage.html",
resolve: {
AutoLoginCheck: ['$state','$window', '$q','$timeout', function ($state, $window,$q,$timeout) {
if($window.localStorage.access_token != null)
{
if($window.sessionStorage.access_token == null) {
$window.sessionStorage.access_token = $window.localStorage.access_token;
}
UserInfoService.SetUserAuthenticated(true);
$timeout(function() {
$state.go('app.home')
},0);
return $q.reject()
}
}]
}
})
This would work for you.
There can be two solutions to your problem
Firstly you can emit an event and the listener will handle your state transition. You can implement the listener in anywhere in a parent controller
Secondly you can implement the $stateChangeStart hook and check your redirection condition there
$rootScope.$on('$stateChangeStart', function (event, toState) {
if (toState.name === 'landingpage') {
if (!isAuthenticated()) { // Check if user allowed to transition
event.preventDefault(); // Prevent migration to default state
$state.go('home.dashboard');
}
}
});
You can use $location.url('/') instead.
You can use resolve to provide your controller with content or data that is custom to the state. resolve is an optional map of dependencies which should be injected into the controller.
You could have a controller that checks for the AuthState and have the redirection accordingly.
.state('landingpage', {
abstract: "true",
url: "/landingpage",
templateUrl: "app/landingpage/landingpage.html",
resolve: {
AutoLoginCheck: ['$window', function ($window) {
if($window.localStorage.access_token != null)
{
if($window.sessionStorage.access_token == null) {
$window.sessionStorage.access_token = $window.localStorage.access_token;
}
//assuming userInfoService does the authentication
var isAuthenticated = userInfoService.SetUserAuthenticated(true);
return isAuthenticated;
}
}]
},
controller: ['$state','AutoLoginCheck', function($state, AutoLoginCheck){
if(AutoLoginCheck){
//authenticated
$state.go('app.home');
} else {
//redirect to unauthenticated page
$state.go('....');
}
}]
})
This is an old thread, but I use $location.path() to accomplish redirection inside of a state.resolve() block
Anyway resolve waiting for promise state . Best thing what you can do is return promise and add timeout for your state:
resolve: {
AutoLoginCheck: ['$state', '$window', '$timeout', '$q', function ($state, $window, $timeout, $q) {
var deferred = $q.defer();
if(user.isLogin()){
deferred.resolve();
}else{
$timeout(function(){
$state.go('app.home');
}
deferred.reject();
}
return deferred.promise;
}]
I am using Trello API, which does not return promise. It has callback functions. I want to write a wrapper to it, which will wait till callback is executed and return the data.
I have written below code :
function getLoggedInMember() {
var deferred = $q.defer();
Trello.members.get("me", deferred.resolve);
deferred.promise.then(function (user) {
return user;
});
}
This function is in my Member Service. Below is my routeConfig :
angular.module('trelloDashboard').config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'Views/Login.html',
controller: 'LoginController'
})
.when('/Dashboard', {
templateUrl: 'Views/Dashboard.html',
controller: 'DashboardController',
resolve: {
user: function (MemberService) {
return MemberService.getLoggedInMember();
}
}
});}]);
This is obviously not working. Is there a way to achieve what I am trying to do here?
Thank you.
Try adding
deferred.promise.then(function (user) {
return user;
},function(error){
console.log("Error");
console.log(error);
//ur return statement
});
Not tested, but try:
.when('/Dashboard', {
templateUrl: 'Views/Dashboard.html',
controller: 'DashboardController',
resolve: {
user: MemberService.getLoggedInMember
}
But your getLoggedInMember function should return a promise.
According to angular docs:
resolve - {Object.=} - An optional map of dependencies which should be injected into the controller. If any of these dependencies are promises, the router will wait for them all to be resolved or one to be rejected before the controller is instantiated. If all the promises are resolved successfully, the values of the resolved promises are injected and $routeChangeSuccess event is fired. If any of the promises are rejected the $routeChangeError event is fired.
Here is what worked!
function getLoggedInMember() {
var deferred = $q.defer();
Trello.members.get("me", function(user){ deferred.resolve(user); });
return deferred.promise;
}
No change was needed in route config.
Thank you, #Benjamin :)
I am new to AngularJS. I made a simple app that have a login function using AngularJS. I used routing and on resolve i put some logic to check if user is logged in and then only proceed accordingly. I have everything working fine, the problem is, when i am not logged in, if i browse to /home it doesn't load the main.html page(that's how it's supposed to be) but a GET request gets called and that returns content of main.html in console.My code looks like this:
app.config(function($routeProvider){
$routeProvider.when('/', {
templateUrl: 'partials/login.html',
controller: 'LoginCtrl',
resolve:{
test: function($http, $q,$location){
var defer = $q.defer();
//checks if user is logged and returns boolean
$http.post('login/getLoggedUser', {}, {}).success(function(data){
if(!data.logged){
defer.resolve(data);
$location.url('/');
}
else{
defer.resolve(data);
$location.url('/home')
}
});
return defer.promise;
}
}
})
.when('/home',{
templateUrl: 'partials/main.html',
controller: 'MainCtrl',
resolve:{
test: function($http, $q,$location){
var defer = $q.defer();
$http.post('login/getLoggedUser', {}, {}).success(function(data){
if(data.logged){
defer.resolve(data);
$location.url('/home');
}
else{
defer.resolve(data);
$location.url('/')
}
});
return defer.promise;
}
},
})
.otherwise({ redirectTo: '/' });
});
When i direct to /home, GET http:/localhost:8080/an-grails/partials/main.html is called in console which contains the content of main page. How do i disable this call? Is there any other method to do this? I read documentation on AngularJS official page and also watched few videos of Egghead.io about resolve and got idea that controller and template gets loaded only after resolve is processed, So what am i doing wrong?
The simplest way to manage rights in your different routes is to catch the $routeChangeStart which is fired by the $route service everytime the route is changed.
With this, you can access the actual route and the next one. This object is the same that you register with $routeProvider.when(). You just have to add a boolean and compare this boolean with the actual user status.
$rootScope.$on("$routeChangeStart", function(event, next, current) {
if (next.loggedOnly && !user.isLogged()) {
// You should implement a isLogged method to test if the user is logged
$location.replace();
// This prevent a redirect loop when going back in the browser
return $location.path("/");
}
}
And inside your route declaration use :
$routeProvider.when('/home', {
templateUrl: 'partials/main.html',
controller: 'MainCtrl',
loggedOnly: true
});