AngularJs-取消路线更改事件


88

如何在AngularJs中取消路线更改事件?

我当前的代码是

$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  

}
});

即使验证失败,Angular也会提取下一个模板和关联的数据,然后立即切换回上一个视图/路线。如果验证失败,我不希望angular提取下一个模板和数据,理想情况下应该没有window.history.back()。我什至尝试过event.preventDefault()但没有用。

Answers:


184

代替$routeChangeStart使用$locationChangeStart

这是angularjs专家对此的讨论:https : //github.com/angular/angular.js/issues/2109

编辑3/6/2018您可以在文档中找到它:https : //docs.angularjs.org/api/ng/service/$location#event-$locationChangeStart

例:

$scope.$on('$locationChangeStart', function(event, next, current) {
    if ($scope.form.$invalid) {
       event.preventDefault();
    }
});

8
问题在于,无法访问路由参数集合。如果您要验证路由参数,则此解决方案不好。
KingHypocrites

1
请注意,当使用$routeChangeStartnext变量只是一个字符串,它不能包含任何数据(例如,你不能访问先前定义的authorizedRoles变量)
MyTitle

@KingOfHypocrites您无法获取路线参数,但可以获取$location.path()$location.search()
Adrian

2
如果要跟踪所有路由更改,是否可以在rootScope上执行此操作?还是有一个更可口的选择?
maxm 2015年

38

更完整的代码示例,使用 $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); 
      });
    }
  );

我对将其挂接到我的根控制器(顶级控制器)中并不完全满意。如果有更好的模式,我很想知道。我是棱角新手:-)


尽管我并不想像折纸海报那样取消路线更改,但这对我来说非常有用。谢谢!
Jim Clouse 2014年

4
是的,rootScope的问题是您必须记住在控制器消失时取消绑定该处理程序。
lostintranslation 2015年

12

一种解决方案是广播“ notAuthorized”事件,并将其捕获到主范围中以重新更改位置。我认为这不是最佳解决方案,但对我有用:

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

在我的主控制器中:

    $scope.$on('notAuthorized', function(){
        $location.path('/forbidden');
    });

注意:在Angular网站上有一些关于此问题的讨论,尚未解决:https : //github.com/angular/angular.js/pull/4192

编辑:

为了回答评论,这里是有关LoginService工作的更多信息。它包含3个功能:

  1. login()(名称具有误导性)向服务器发出请求,以获取有关(先前)记录的用户的信息。还有另一个登录页面,该页面仅填充服务器中的当前用户状态(使用SpringSecurity框架)。我的Web服务并不是真正的无状态的,但是我更喜欢让那个著名的框架来处理我的安全性。
  2. isAuthenticated()仅搜索客户端会话中是否填充了数据,这意味着它已在(*)之前进行了身份验证
  3. isAuthorized()处理了访问权限(超出本主题的范围)。

(*)更改路线时,将填充我的会话。我已经覆盖了when()方法,以便在为空时填充会话。

这是代码:

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

您能否说一下LoginService.isAuthenticated()首页加载后返回的内容?您如何储存currentUser?如果用户刷新页面会发生什么情况(用户需要再次输入凭据)?
MyTitle 2014年

我在原始答案中添加了有关LoginService的更多信息。服务器提供currentUser,并且路由更改可处理任何页面刷新,无需用户再次登录。
Asterius

4

对于任何绊倒这个问题的人(至少在角度1.4中),您可以这样做:

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

6
我想知道,使用花括号或“;”是否需要额外付费?
塞尔·夏普

6
@MatthiasJansen当然。最重要的是,花括号数是两倍,分号数是三倍。
阿斯科斯

1

这是我的解决方案,适用于我,但我不知道我是否采用正确的方法,因为我是Web技术的新手。

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

2
如果不确定答案,如何回答问题?
2014年

我敢肯定它能起作用。我不确定这是否正确。如果您有更好的解决方案,我希望看到它。
Alexandrakis alexandros 2014年

1

我发现这很重要

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

我的帖子将来可能会对您有所帮助。


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

2
尽管此代码段可以解决问题,但提供说明确实有助于提高您的帖子质量。请记住,您将来会为读者回答这个问题,而这些人可能不知道您提出代码建议的原因。
dpr

0

万一您需要在$routeChangeStart事件中停止更改路由(例如,您想根据下一条路由执行某些操作),请注入$route并进行内部$routeChangeStart调用:

$route.reload()

1
我满怀希望,但是在Chrome的Angular 1.2.7中,这似乎会导致JS循环,并且页面冻结。
Nick Spacek 2014年

1
@NickSpacek您调用的条件$route.reload()必须不同,否则它将再次运行相同的代码。这等效于以条件为条件创建while循环true
凯文·比尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.