AngularJs - cancel route change event
Solution 1:
Instead of $routeChangeStart
use $locationChangeStart
Here's the discussion about it from the angularjs guys: https://github.com/angular/angular.js/issues/2109
Edit 3/6/2018 You can find it in the docs: https://docs.angularjs.org/api/ng/service/$location#event-$locationChangeStart
Example:
$scope.$on('$locationChangeStart', function(event, next, current) {
if ($scope.form.$invalid) {
event.preventDefault();
}
});
Solution 2:
A more complete code sample, using $locationChangeStart
// assuming you have a module called app, with a
angular.module('app')
.controller(
'MyRootController',
function($scope, $location, $rootScope, $log) {
// your controller initialization here ...
$rootScope.$on("$locationChangeStart", function(event, next, current) {
$log.info("location changing to:" + next);
});
}
);
I'm not completely happy with hooking this up in my root controller (top level controller). If there is a better pattern, I'd love to know. I'm new to angular :-)
Solution 3:
A solution is to broadcast a 'notAuthorized' event, and catch it in the main scope to re-change the location. I think it is not the best solution, but it worked for me:
myApp.run(['$rootScope', 'LoginService',
function ($rootScope, LoginService) {
$rootScope.$on('$routeChangeStart', function (event, next, current) {
var authorizedRoles = next.data ? next.data.authorizedRoles : null;
if (LoginService.isAuthenticated()) {
if (!LoginService.isAuthorized(authorizedRoles)) {
$rootScope.$broadcast('notAuthorized');
}
}
});
}
]);
and in my Main Controller:
$scope.$on('notAuthorized', function(){
$location.path('/forbidden');
});
Note: there is some discussion about this problem on angular site, not yet solved: https://github.com/angular/angular.js/pull/4192
EDIT:
To answer the comment, here is more information about the LoginService works. It contains 3 functions:
- login() (name is misleading) do a request to the server to get information about the (previously) logged user. There is another login page which just populate the current user state in the server (using SpringSecurity framework). My Web Services are not truely stateless, but I preferred to let that famous framework handle my security .
- isAuthenticated() just search if the client Session is filled with data, which means it has been authenticated before (*)
- isAuthorized() handled access rights (out of scope for this topic).
(*) My Session is populated when the route change. I have overridden then when() method to populate the session when empty.
Here is the code :
services.factory('LoginService', ['$http', 'Session', '$q',
function($http, Session, $q){
return {
login: function () {
var defer = $q.defer();
$http({method: 'GET', url: restBaseUrl + '/currentUser'})
.success(function (data) {
defer.resolve(data);
});
return defer.promise;
},
isAuthenticated: function () {
return !!Session.userLogin;
},
isAuthorized: function (authorizedRoles) {
if (!angular.isArray(authorizedRoles)) {
authorizedRoles = [authorizedRoles];
}
return (this.isAuthenticated() && authorizedRoles.indexOf(Session.userRole) !== -1);
}
};
}]);
myApp.service('Session', ['$rootScope',
this.create = function (userId,userLogin, userRole, userMail, userName, userLastName, userLanguage) {
//User info
this.userId = userId;
this.userLogin = userLogin;
this.userRole = userRole;
this.userMail = userMail;
this.userName = userName;
this.userLastName = userLastName;
this.userLanguage = userLanguage;
};
this.destroy = function () {
this.userId = null;
this.userLogin = null;
this.userRole = null;
this.userMail = null;
this.userName = null;
this.userLastName = null;
this.userLanguage = null;
sessionStorage.clear();
};
return this;
}]);
myApp.config(['$routeProvider', 'USER_ROLES', function ($routeProvider, USER_ROLES) {
$routeProvider.accessWhen = function (path, route) {
if (route.resolve == null) {
route.resolve = {
user: ['LoginService','Session',function (LoginService, Session) {
if (!LoginService.isAuthenticated())
return LoginService.login().then(function (data) {
Session.create(data.id, data.login, data.role, data.email, data.firstName, data.lastName, data.language);
return data;
});
}]
}
} else {
for (key in route.resolve) {
var func = route.resolve[key];
route.resolve[key] = ['LoginService','Session','$injector',function (LoginService, Session, $injector) {
if (!LoginService.isAuthenticated())
return LoginService.login().then(function (data) {
Session.create(data.id, data.login, data.role, data.email, data.firstName, data.lastName, data.language);
return func(Session, $injector);
});
else
return func(Session, $injector);
}];
}
}
return $routeProvider.when(path, route);
};
//use accessWhen instead of when
$routeProvider.
accessWhen('/home', {
templateUrl: 'partials/dashboard.html',
controller: 'DashboardCtrl',
data: {authorizedRoles: [USER_ROLES.superAdmin, USER_ROLES.admin, USER_ROLES.system, USER_ROLES.user]},
resolve: {nextEvents: function (Session, $injector) {
$http = $injector.get('$http');
return $http.get(actionBaseUrl + '/devices/nextEvents', {
params: {
userId: Session.userId, batch: {rows: 5, page: 1}
},
isArray: true}).then(function success(response) {
return response.data;
});
}
}
})
...
.otherwise({
redirectTo: '/home'
});
}]);
Solution 4:
For anyone stumbling upon this is an old question, (at least in angular 1.4) you can do this:
.run(function($rootScope, authenticationService) {
$rootScope.$on('$routeChangeStart', function (event, next) {
if (next.require == undefined) return
var require = next.require
var authorized = authenticationService.satisfy(require);
if (!authorized) {
$rootScope.error = "Not authorized!"
event.preventDefault()
}
})
})
Solution 5:
This is my solution and it works for me but i don't know if i am on the right way cause i am new to web technologies.
var app = angular.module("app", ['ngRoute', 'ngCookies']);
app.run(function($rootScope, $location, $cookieStore){
$rootScope.$on('$routeChangeStart', function(event, route){
if (route.mustBeLoggedOn && angular.isUndefined($cookieStore.get("user"))) {
// reload the login route
jError(
'You must be logged on to visit this page',
{
autoHide : true,
TimeShown : 3000,
HorizontalPosition : 'right',
VerticalPosition : 'top',
onCompleted : function(){
window.location = '#/signIn';
window.setTimeout(function(){
}, 3000)
}
});
}
});
});
app.config(function($routeProvider){
$routeProvider
.when("/signIn",{
controller: "SignInController",
templateUrl: "partials/signIn.html",
mustBeLoggedOn: false
});