I use the following to redirect routes to login if a user is not logged in:
angular.module('app').run(['$rootScope', '$location', '$state', 'AuthService',
function($rootScope, $location, $state, AuthService) {
/* redirect to login if not logged in */
$rootScope.$on( '$stateChangeStart',
function(e, toState, toParams, fromState, fromParams) {
if(toState.name === "login"){
return; // already going to login
}
if(!AuthService.user) {
e.preventDefault();
$state.go('login');
}
});
}]);
How can I pass the url from the "prevented" state to the login state so that I can continue navigating to that state once logged in?
Example: User clicks link for myapp.com/#/path/to/data --> redirects to myapp.com/#/login --> user logs in --> user routed to myapp.com/#/path/to/data
I basically need /path/to/data to be sent to my login controller so that I can handle that. I cannot seem to find '/path/to/data' in any of the state or param variables in the $stateChangeStart listener.
The $state service has an href method which can create a URL given the state and its parameters. Since those are provided to the state change listener, you can create a URL and then pass it as a param to the login state.
$rootScope.$on('$stateChangeStart',
function(e, toState, toParams, fromState, fromParams) {
if (toState.name === "login") {
return; // already going to login
}
if (!AuthService.user) {
e.preventDefault();
var redirectUrl = $state.href(toState.name, toParams);
$state.go('login', {
redirect: redirectUrl
});
}
});
You'll need to add a redirect parameter to your login state so you can use that.
You can store the pre-login location in $rootScope, and then fetch it from there once login is successful
if(!AuthService.user) {
e.preventDefault();
$rootScope.pre_login_path = $location.path();
$state.go('login');
}
And then after login, you can set the login path similarly in your login controller/service (have that include dependency injection of $rootScope and $location)
$location.path($rootScope.pre_login_path);
Related
I am trying to redirect to home page when specific views are refreshed in browser. If I handle stateChangeStart event on rootScope level, it all works fine and navigates to home page.
var payApp = angular.module('pay', ['ui.router', 'pay.controllers', 'pay.services', 'exceptionOverride']);
payApp.run(['$templateCache', '$rootScope', '$state', '$stateParams', '$window', '$location', function ($templateCache, $rootScope, $state, $stateParams, $window, $location) {
$templateCache.put(baseUrl, angular.element('#ui-view').html());
// Allows to retrieve UI Router state information from inside templates
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$rootScope.$on('$stateChangeSuccess', function (event, toState) {
// Sets the layout name, which can be used to display different layouts (header, footer etc.)
// based on which page the user is located
$rootScope.layout = toState.layout;
});
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams, options) {
//When payment or order page is refreshed, navigate to home
if (fromState.name == '' && (toState.name == 'payment' || toState.name == 'order')) {
$location.url('/');
}
});
var windowElement = angular.element($window);
windowElement.on('beforeunload', function (event) {
var path = $location.path();
if (path == '/payment' || path == '/order') {
event.preventDefault();
}
});
}]);
But I am trying to move this logic to a specific controller level so the stateChangeStart event will be fired only for specific views which are using this controller.
var payControllers = angular.module('pay.controllers', ['ui.bootstrap', 'ngIdle']);
payControllers.controller('payPaymentCtrl', ['$scope', '$state', '$window', '$location', 'payDataService', 'Idle', 'Keepalive', '$uibModal',
function ($scope, $state, $window, $location, service, Idle, Keepalive, $uibModal) {
var windowElement = angular.element($window);
windowElement.on('beforeunload', function (event) {
var path = $location.path();
if (path == '/payment' || path == '/paymentcreated') {
event.preventDefault();
}
});
$scope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams, options) {
//When payment or order page is refreshed, navigate to home
//Both payment and order share the same controller.
if (fromState.name == '' && (toState.name == 'payment' || toState.name == 'order')) {
$location.url('/');
}
});
$scope.paymentOption = service.GetpaymentOption();
if ($scope.paymentOption == null) {
//$state.go('home', {}, { reload: true });
//$location.path('/').replace();
$location.url('/');
}
}]);
The beforeunload event fires in the controller when a view is refreshed in browser but stateChangeStart does not fire.
Also, I tried to get rid of the stateChangeStart event from the controller level and handle to go to home page when some crucial data is missing. It gives me an error saying "Unable to load property of undefined". It appears to be trying to load some data that is bound to angular scope elements which usually appears upon some operation on home page. I need it to look like just the way when I open the home page for the first time when the application is run.
Thanks for any suggestions.
In my application I am trying to redirect the user to get redirected to login if he is not logged in and if he is than restrict him from visiting login page And I implemented the same like this before as well but now its not working I would like to know what I am doing wrong
app.run(function ($rootScope, $state, authService) {
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
if (toState === 'home' && authService.isAuthenticated()) {
event.preventDefault();
$state.go(fromState);
}
if (toState !== 'home' && !authService.isAuthenticated()) {
event.preventDefault();
$state.go('home');
}
});
});
I have done same here as well but it was working I don't know why that was working and this is not working.
The link for the code that was working
Using ui-router how can I accomplish the following URL change? For example, when a user goes to "/page?hello=True" I want the url to change to "/page?status=hello".
$stateProvider.state('page', {
url: '/page/:id?status',
controller: 'MyCtrl as myCtrl',
templateProvider: function($templateCache) {
return $templateCache.get('templates/route.html');
}
});
You can use the stateChangeStart event in the app.run method to listen for state changes into that route, and change anything you want on the params. Something like this, might need to play with toState and toParams to get it right for you...
.run(['$state', '$rootScope', function($state, $rootScope) {
$rootScope.$on('$stateChangeStart', function(e, toState, toParams, fromState, fromParams) {
if (toState.name === 'invitation') {
//change toParams to whatever you want
}
});
});
I'm loading states dynamically based on the current user role. But when the page is refreshed it takes to the 404 page. Also in $stateChangeStart event fromState.name is blank.
Is there a way to go to the state before refresh button was clicked? Should I store the state before refresh is pressed and then use it?
.state('404', {
url: '/404',
templateUrl: '404.tmpl.html',
controller: function ($scope, $state, APP) {
$scope.app = APP;
$scope.goHome = function () {
$state.go('default.page');
};
}
})
$urlRouterProvider.otherwise('/404');
....
$rootScope.$on('$stateChangeStart', function (e, toState, toParams, fromState, fromParams) {
//fromState.name = '' on refresh
});
Thanks in advance!
The below seems to work, not sure if this is the best approach. On before unload event save the state, then use it in $stateChangeStart.
.run(function ($rootScope, $window, $state, authService, $http, $timeout, localStorageService) {
$rootScope.$on('$stateChangeStart', function (e, toState, toParams, fromState, fromParams) {
if (authService.isLoggedIn()) {
if (toState.name === "404" && fromState.name === '' && localStorageService.get("LAST_STATE") !== "404") {
authService.loadStates($stateProviderRef).then(function () {
$state.go(localStorageService.get("LAST_STATE"), localStorageService.get("LAST_STATE_PARAMS"));
});
}
}
});
window.onbeforeunload = function () {
localStorageService.set("LAST_STATE", $state.current.name);
localStorageService.set("LAST_STATE_PARAMS", $state.params);
return null;
}
})
I have problem with my angular test.
describe('Routes test', function() {
//Initialize global variables
var $state,
$location;
// Load the main application module
beforeEach(module("MyApp"));
beforeEach(inject(function(_$location_, _$state_, $templateCache) {
$state = _$state_;
$location = _$location_;
// We need add the template entry into the templateCache if we ever
// specify a templateUrl
$templateCache.put('signin.admin.view.html', 'signin');
}));
it('should be redirect to signin if user is not loggedin', function() {
$rootScope.$apply(function() {
$location.path('/');
});
expect($state.current.name).toEqual('signin');
console.log($state.current);
});
});
So I expect signin state because user is not logged in. But from console $state.current is still:
Object{name: '', url: '^', views: null, abstract: true}
To redirect I used this code:
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
if (toState.name.indexOf('signin') === 0 ) {
if( loggedIn ) {
event.preventDefault();
$state.go('layout.dashboard');
}
} else {
if( !loggedIn ) {
event.preventDefault();
$state.go('signin');
}
}
});
Variable loggedIn is false. So I dont understand, when I make test manually in browser $state.current is as expected. But not in this test.
Thank you for your help.
Rather than doing $rootScope.$apply(...) try doing:
$location.path('/');
$rootScope.$broadcast('$locationChangeSuccess');
UI Router listens to the $locationChangeSuccess event that is fired from angular's $location service. When it catches this event it will find the correct state and begin navigating to that state, hence firing the $stateChangeStart event.
Hope this helps!