【问题标题】:AngularJs - cancel route change eventAngularJs - 取消路线更改事件
【发布时间】:2013-04-26 23:45:45
【问题描述】:

如何在 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() 但没有用。

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    $locationChangeStart代替$routeChangeStart

    这是来自 angularjs 家伙的讨论:https://github.com/angular/angular.js/issues/2109

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

    例子:

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

    【讨论】:

    • 这样做的问题是无法访问路由参数集合。如果您尝试验证路由参数,则此解决方案不好。
    • 请注意,当使用$routeChangeStart时,next变量只是一个字符串,它不能包含任何数据(例如你不能访问之前定义的authorizedRoles变量)
    • @KingOfHypocrites 你不能获取路由参数,但是你可以获取$location.path()$location.search()
    • 如果您想跟踪所有路由更改,您/是否建议在 rootScope 上执行此操作?还是有更美味的替代品?
    【解决方案2】:

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

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

    【讨论】:

    • 这对我很有用,虽然我并没有像原始海报那样取消我的路线更改。谢谢!
    • 是的,rootScope 的问题是你必须记住在你的控制器消失时取消绑定该处理程序。
    【解决方案3】:

    一种解决方案是广播“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() 只是搜索客户端Session是否填充了数据,这意味着它之前已经过身份验证(*)
    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?如果用户刷新页面会发生什么(用户需要再次重新输入凭据)?
    • 我在原始答案中添加了有关 LoginService 的更多信息。 currentUser由服务器提供,路由变化处理任何页面刷新,用户无需再次登录。
    【解决方案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()
                }
            })
          })
    

    【讨论】:

    • 我想知道,使用大括号或“;”是否会收取额外费用?
    • @MatthiasJansen 当然。最重要的是,大括号是双倍的,分号是三倍的。
    【解决方案5】:

    这是我的解决方案,它适用于我,但我不知道我是否走在正确的道路上,因为我是网络技术的新手。

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

    【讨论】:

    • 如果您不确定自己的答案,如何回答问题?
    • 我确信它有效。我不确定这是否是正确的方法。如果您有更好的解决方案,我想看看。
    【解决方案6】:

    我发现这个相关

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

    我的帖子将来可能会对某些人有所帮助。

    【讨论】:

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

      【讨论】:

      • 虽然这段代码 sn-p 可以解决问题,including an explanation 确实有助于提高您的帖子质量。请记住,您是在为将来的读者回答问题,而这些人可能不知道您提出代码建议的原因。
      【解决方案8】:

      如果您需要在$routeChangeStart 事件中阻止路由更改(即您想根据下一条路由执行一些操作),请注入$route 和内部@987654323 @调用:

      $route.reload()
      

      【讨论】:

      • 我很有希望,但在 Chrome 上的 Angular 1.2.7 中,这似乎会导致 JS 循环并且页面冻结。
      • @NickSpacek 您调用$route.reload() 的条件必须不同,否则它会再次运行相同的代码。这相当于创建一个以true 为条件的while 循环。
      【解决方案9】:

      只是为了分享,就我而言,我希望 使用 $routeChangeStart 延迟路由解析。我有一个SomethingService,必须在路由解析开始之前加载(是的,健谈的应用程序),因此我有一个等待的承诺。也许我发现了一个黑客......如果解决返回拒绝,则路线的解决会出错。我破坏了解析配置,稍后我会修复它。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-05
        • 1970-01-01
        • 1970-01-01
        • 2013-12-28
        • 2013-04-15
        相关资源
        最近更新 更多