简体   繁体   English

$ state.go不在诺言工作

[英]$state.go not working from promise

I am trying to implement a basic security check to block users from accessing certain states depending on their permission set: 我正在尝试实施基本安全检查,以阻止用户访问某些状态,具体取决于他们的权限集:

'use strict';

var autoExecApp = angular.module("myApp", ['ui.router']);

autoExecApp.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider){
    $stateProvider
        .state('index', {
            url: '/index',
            templateUrl: 'partials/index.html'
        })
        .state('permission', {
            url: '/permission',
            templateUrl: 'partials/permissions.html'
        });
}]);

autoExecApp.run(['$rootScope', '$state', '$userRoles', function($rootScope, $state, $userRoles) {

    $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {

        event.preventDefault(); // prevent page from being loaded anyway
        $userRoles.hasPermissions(toState.name).then(function(data) {
            var result = data === "true";
            if (!result) {
                $state.go('permission');
            }
            else {
                $state.go(toState.name, toParams ,{notify:false});
            }
        });
    });
}]);

Now the problem surrounds the fact that my state change happens inside of a promise. 现在问题围绕着我的状态变化发生在承诺内部的事实。 I would happily make it synchronous - as it really should be in this case but I am confused as to why it is not working asynchronously. 我很乐意让它同步 - 因为它确实应该在这种情况下,但我很困惑为什么它不是异步工作。

If I move the event.preventDefault() inside the promise, it works. 如果我在promise中移动event.preventDefault() ,它就可以了。 BUT does the state change in 2 steps. 但是状态改变分为两步。 First, it will always go to the original page and then it will soon after transition to the 'blocked' page (if it was blocked). 首先,它将始终转到原始页面,然后在转换到“已阻止”页面后很快(如果它被阻止)。 I gather this is because it returns successfully once from the $on and updates the state, and then updates it again when it returns from the promise. 我收集这是因为它从$ on成功返回一次并更新状态,然后在从promise返回时再次更新它。

If the promise is how you see it however, the state will not change. 如果承诺是你如何看待它,状态将不会改变。 Everything appears to be working, but the page will not update the view. 一切似乎都有效,但页面不会更新视图。 I am not sure why this is the case. 我不确定为什么会这样。 For some reason, if the event is not prevented, the later state change will work. 出于某种原因,如果未阻止事件,则稍后的状态更改将起作用。 However, if it is prevented and the $state.go is called from the returned promise, it appears to be too late to update the $state. 但是,如果它被阻止并且从返回的promise中调用$ state.go,则更新$ state似乎为时已晚。

EDIT: I had success using the broadcast solution and this was what it looked like: 编辑:我使用广播解决方案取得了成功,这就是它的样子:

event.preventDefault();
$state.go(toState.name, toParams, {notify: false}).then(function() {
     // line 907 state.js
     $rootScope.$broadcast('$stateChangeSuccess', toState, toParams, fromState, fromParams);
});

Apparently due to it being in a promise, the preventDefault(); 显然由于它是一个承诺,preventDefault(); suppresses this '$stateChangeSuccess' from naturally firing. 从自然射击中抑制这个'$ stateChangeSuccess'。 Performing it manually restores the expected behaviour. 手动执行它可以恢复预期的行为。

Its a known issue. 这是一个已知的问题。 The workaround that has generally worked is to broadcast a custom event from within '$stateChangeStart' handler and then do your state transition from within that custom event's handler. 一般工作的解决方法是从'$ stateChangeStart'处理程序中广播自定义事件,然后从该自定义事件的处理程序中进行状态转换。

$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {

    event.preventDefault(); // prevent page from being loaded anyway
    $userRoles.hasPermissions(toState.name).then(function(data) {
        var result = data === "true";
        if (!result) {

            $rootScope.$broadcast('goToPermission');
        }
        else {
            $rootScope.$broadcast('goToState', toState.name);
        }
    });
});

borrowed from: https://github.com/angular-ui/ui-router/issues/178#issuecomment-49156829 借鉴自: https//github.com/angular-ui/ui-router/issues/178#issuecomment-49156829

I think that Vladimir Gurovich's "broadcast" solution is the way to go, especially as he indicates it to be an established workaround. 我认为弗拉基米尔·古罗维奇的“广播”解决方案是要走的路,特别是因为他表示这是一个既定的解决方案。

But I wonder if a solution in which permissions are cached then looked up synchronously , might also be viable. 但我想知道一个解决方案,其中缓存权限然后同步查找,也可能是可行的。

autoExecApp.run(['$rootScope', '$state', '$userRoles', function($rootScope, $state, $userRoles) {
    // Create a permissions cache, seeded with 
    // permission to go to the 'permission' state.
    var permissionCache = {
        'permission': true
    };

    // Create a researched list of all possible state names.
    var stateNames = ['stateA', 'stateB', 'stateC'];

    // Now load the cache asynchronously with 
    // `true` for each permission granted.
    stateNames.forEach(function(state) {
        $userRoles.hasPermissions(state).then(function(data) {
            if(data === "true") {
                permissionCache[state] = true;
            }
        });
    });

    // And finally, establish a $stateChangeStart handler 
    // that looks up permissions **synchronously**, 
    // in the permissionCache.
    $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
        if (!permissionCache[toState.name]) {
            event.preventDefault(); // prevent page from being loaded
            $state.go('permission');
        }
    });
}]);

Of course, this solution would have its own issues : 当然,这个解决方案会有自己的问题:

  • relies on good research of all possible state names. 依赖于对所有可能的州名的良好研究。
  • any state change attempted before the cache is loaded, will be rejected. 在加载缓存之前尝试的任何状态更改都将被拒绝。
  • any change of userRoles in the life of the page would require the cache to be cleared and reloaded. 页面生命周期中userRoles的任何更改都需要清除和重新加载缓存。

Without research/testing, I can't say whether these issues are significant or not. 没有研究/测试,我不能说这些问题是否重要。

Why don't you use a resolve function to check if the user can access your route? 为什么不使用解析功能来检查用户是否可以访问您的路线?
Something like this: 像这样的东西:

autoExecApp.config(['$stateProvider', '$urlRouterProvider', function (
    $stateProvider, $urlRouterProvider) {
    $stateProvider
        .state('index', {
            url: '/index',
            templateUrl: 'partials/index.html'
        })
        .state('permission', {
            url: '/permission',
            templateUrl: 'partials/permissions.html'
        })
    .state('restricted-route', {
      url: '/restricted',
      templateUrl: 'partials/restricted.html',
      resolve: {
        authenticated: function ($q) {
          var deferred = $q.defer();
          $userRoles.hasPermissions(this)
            .then(function (data) {
              var result = data === "true";
              if (result) {
                deferred.resolve();
              } else {
                deferred.reject('unauthorized');
              }
            });
          return deferred.promise;
        }
      }
    });
}]);

$rootScope.$on('$stateChangeError',
  function (event, toState, toParams, fromState, fromParams, rejection) {

    if (rejection.error === 'unauthorized') {
      $state.go('permission');
    }
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM