AngularJs-取消路线更改事件

如何取消 AngularJ 中的路线更改事件?

我现在的代码是

$rootScope.$on("$routeChangeStart", function (event, next, current) {


// do some validation checks
if(validation checks fails){


console.log("validation failed");


window.history.back(); // Cancel Route Change and stay on current page


}
});

即使验证失败,角度拉下一个模板和相关的数据,然后立即切换回以前的视图/路由。如果验证失败,我不希望在下一个模板和数据拉角,理想情况下应该没有 window.history. back ()。我甚至尝试了 event. proventDefault () ,但是没有用。

99200 次浏览

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();
}
});

In case you need to do stop the route from changing in the $routeChangeStart event (i.e. you want to perform some operation based on the next route), inject $route and inside $routeChangeStart call:

$route.reload()

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 :-)

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:

  1. 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 .
  2. isAuthenticated() just search if the client Session is filled with data, which means it has been authenticated before (*)
  3. 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'
});
}]);

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
});

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()
}
})
})

i found this one relevant

var myApp = angular.module('myApp', []);


myApp.run(function($rootScope) {
$rootScope.$on("$locationChangeStart", function(event, next, current) {
// handle route changes
$rootScope.error = "Not authorized!"
event.preventDefault()
});
});

my post may help some one in future.

var app=angular
.module('myapp', [])
.controller('myctrl', function($rootScope) {
$rootScope.$on("locationChangeStart", function(event, next, current) {
if (!confirm("location changing to:" + next)) {
event.preventDefault();
}
})
});

Just to share, in my case I want to delay route resolution with $routeChangeStart. I've got a SomethingService that must load before the resolution of the route starts (yes, chatty application) hence I've got a promise to wait for. Maybe I found an hack... The resolution of the route goes in error if a resolve return a rejection. I broken the resolve configuration and I fix it back later.

    var rejectingResolve = {
cancel: function ($q){
// this will cancel $routeChangeStart
return $q.reject();
}
}
    

$rootScope.$on("$routeChangeStart", function(event, args, otherArgs) {
var route = args.$$route,
originalResolve = route.resolve;
    

if ( ! SomethingService.isLoaded() ){


SomethingService.load().then(function(){
// fix previously destroyed route configuration
route.resolve = originalResolve;
                

$location.search("ts", new Date().getTime());
// for redirections
$location.replace();
});


// This doesn't work with $routeChangeStart:
// we need the following hack
event.preventDefault();
            

// This is an hack!
// We destroy route configuration,
// we fix it back when SomethingService.isLoaded
route.resolve = rejectingResolve;
}
});