【问题标题】:event.preventDefault() not waiting till promise is resolvedevent.preventDefault() 不等到承诺解决
【发布时间】:2015-07-12 13:36:01
【问题描述】:

我正在使用 event.preventDefault() 技术根据我从后端获取的标志值有条件地将用户重定向到适当的状态。 Eveything 似乎工作正常,除了有时会呈现实际页面然后发生重定向,而且在极少数情况下也是如此。实际页面的这种渲染时间也很短。以下是我正在使用的代码 sn-p:

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

  if (fromState.name === '' && toState.name === "myState"){

    Auth.isLoggedInAsync(function(loggedIn) {
      if (!loggedIn) {
        event.preventDefault();
        $state.go('state1');
      } else {
          event.preventDefault();
          Auth.getNextStateForUser()
          .then(function(data) {
            console.log(data.nextState);
            $state.go(data.nextState);
          })
          .catch(function(err){
              console.log(err);
              $state.go(toState.name);
          });
      }
    });

我遵循的逻辑是,检查用户是否登录,然后重定向到 state1,否则根据从后端获取的 nextstateValue 重定向到下一个适当的状态。如果出现错误,直接打开实际页面,不做任何重定向。

我希望我清楚自己在做什么。只是想知道我所面临的问题是真正的问题还是我在做错了什么。

我也将它发布在 github 页面上,但似乎我是唯一一个面临这个问题的人。

请检查此以获取更多信息: https://github.com/angular-ui/ui-router/issues/2088

谢谢

编辑:根据答案,我更新了使用 ui-router 解析的代码并将我的 isLoggedInAsyc 转换为返回承诺。

resolve: {

  promiseState: function(Auth, $state, $q, $stateParams) {

    var deferred = $q.defer();

    Auth.isLoggedInSync()
    .then(function(isLoggedIn){
        console.log(isLoggedIn);
        if(isLoggedIn) {
          Auth.getNextStateForUser()
              .then(function(data) {
                console.log(data.nextState);
                $state.go(data.nextState);
                deferred.resolve(data);
               })
              .catch(function(error) {
                console.log(error);
                deferred.reject(error);
          }); 
        }

        deferred.resolve(isLoggedIn);
    })
    .catch(function(err){
        console.log(err);
        deferred.reject(err);
    });

    return deferred.promise;
  }
}

我的 isLoggedIn() 返回承诺。

  isLoggedInSync: function(callback) {
    var cb = callback || angular.noop;
    var deferred = $q.defer();
    if(currentUser.hasOwnProperty('$promise')) {
      currentUser.$promise.then(function() {
        deferred.resolve(true);
        return cb(true);
      }).catch(function() {
        deferred.resolve(false);
        return cb(false);
      });
    } else if(currentUser.hasOwnProperty('role')) {
      deferred.resolve(true);
    } else {
      deferred.resolve(false);
    }
    return deferred.promise;
  }

【问题讨论】:

  • 我认为角码/事件应该是$event 而不是event
  • 我们之前在代码中遇到过这个问题。处理这种逻辑的最好方法是将角色重定向放在resolve 中。这些可以异步完成,并且所有解析处理程序必须被调用并具有结果(返回的承诺也必须被解析)才能切换到状态。

标签: javascript angularjs express angular-ui-router


【解决方案1】:

正如@milaenlempera 所提到的,所有 Angular 事件都是同步的,在这种情况下,您没有真正的方法可以很好地处理异步。我们在生产代码中遇到了这个问题,我们的解决方案是在 ui-router 中使用 resolve 功能。这就是我们解决它的方法(显然插入到您的代码中)

.state({
  name: 'myState',
  resolve: {
    principal: ['Auth', '$state', '$q', function(Auth, $state, $q) {
      var deferred = $q.defer();
      Auth.isLoggedInAsync(function(loggedIn) {
        if(!isLoggedIn) {
          deferred.resolve($state.go('state1'));
          return;
        }
        return Auth.getNextStateForUser()
          .then(function(data) {
            $state.go(data.nextState);
           })
          .catch(function(error) {
            deferred.reject(error);
          });
      });
      return deferred.promise;
    }]
  }
});

我建议转Auth.isLoggedInAsync 以返回承诺。

另外,如果Auth.getNextStateForUser() 被拒绝,您的原始代码将导致无限循环。它将尝试转到名称为toState.name 的状态,也就是刚刚导致错误的状态,这将导致另一个错误并导致它转到toState.name....

因为resolve 级联,如果你想共享权限规则,你可以实现子状态。没有办法只在状态上放置一个属性并在其他地方处理异步权限检查,这确实很糟糕,但这就是我们目前所处理的。

根据 cmets,这是一个使用修改后的代码链接承诺的示例。

  promiseState: ['Auth', '$state', function(Auth, $state) {
    return Auth.isLoggedInSync()
      .then(function(isLoggedIn) {
        if(isLoggedIn) {
          return Auth.getNextStateForUser().then(function(data) {
            return data.nextState;
          });
        }
        // If the user isn't logged in, return the state that you should go to instead
        return 'state1';
      })
      .then(function(nextState) {
        return $state.go(nextState);
      });
  }]

【讨论】:

  • 非常感谢您抽出时间提供帮助。我已经根据您的逻辑更新了我的代码,并将我的 isLoggedIn() 函数转换为返回承诺。你能看看它并告诉我它是否正确吗?刚接触这个概念,只是想交叉验证我是否做对了?谢谢
  • 解决方案是可能的,但我最好在路由定义中使用数据属性data: { authRoles: ['ROLE_ADMIN'] }(请参阅我的答案中的解决方案链接)。 Resolve 有不同的用途(为控制器加载本地依赖项)
  • 我看了你的回答,这本来是我走的第一条路线。但是,我发现当您进行异步身份验证时,这并没有按预期工作并且非常不稳定并且不能顺利运行根本。这就是我建议这种方法的原因。您的示例仅在您具有同步身份验证时才有效。当我们使用这种方法时,由于我们登录的异步性质,我们经常会得到无限的摘要循环和/或不正确的结果。
  • @dark_shadow 对于您更新的解决方案,如果您的AuthSvc.isLoggedIn 返回一个承诺,则无需使用$q,您可以改用承诺链。
  • @DanPantry:你能告诉我你到底想说什么吗?我对 node.js 还很陌生,仍然对 promise 和 $q 的概念有所了解。如果您能向我展示一些代码 sn-ps,那就太好了。谢谢
【解决方案2】:

Angular 事件是同步的。 如果需要停止它,则必须在事件处理函数中同步执行。 在您的示例中是异步检查登录,同时路由继续。这就是您看到页面并在之后发生重定向的原因。

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

      // here check if route can be display (synchronously)
      // or event.preventDefault()

      Auth.isLoggedInAsync(function(loggedIn) {
      if (loggedIn) {
        // continue to original route or go to state by server data
      } else {
        // redirect to login page
      }  
});

你可以从我的解决方案中得到启发here

【讨论】:

  • @milaenlempera:非常感谢您抽出时间提供帮助。我已经用同步的做事方式更新了我的代码。你能看看它并告诉我它是否正确吗?刚接触这个概念,只是想交叉验证我是否做对了?谢谢
  • 乍一看还不错。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-29
  • 2018-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-04
  • 2018-04-02
相关资源
最近更新 更多